Preventing / dealing with double button clicks in angular

In angular we can set up a button to send ajax requests like this in view:

... ng-click="button-click"

and in controller:

...
$scope.buttonClicked = function() {
   ...
   ...
   // make ajax request 
   ...
   ...
}

So to prevent a double submit I could set a flag to buttonclicked = true when a button is click and unset it when the ajax callback is finished. But, even then control is handled back to angular who will updates to the Dom. That means there is a tiny window where the button could be clicked again before the original button click has completely 100% finished.

It's a small window but can still happen. Any tips to completely avoid this from happening - client side i.e. without making any updates to server.

Thanks

First you'd better add ngDblclick, when it detects the double click just return false:

<ANY ng-click="buttonClicked()" ng-dblclick="return false">

If you want to wait for the Ajax call to be finished, then you can disable the button by setting the ng-disabled

<ANY ng-click="buttonClicked()" ng-dblclick="return false;" ng-disabled="flag">

And in your controller, you can do

$scope.flag = false;
$scope.buttonClicked = function() {
    $scope.flag = true;
    Service.doService.then(function(){
        //this is the callback for success
        $scope.flag = false;
    }).error(function(){
        //this is the callback for the error
        $scope.flag = false;
    })
}

You need to handle both case when the ajax call is successfull or failed, since if it is failed, you don't want it show as diabled to confuse user.

Using ng-disabled worked just fine in this example. No matter how furiously I clicked the console message only populated once.

I was curious exactly how long it takes for angular to apply the changes to the buttonDisabled flag. If you check the console in the plunker example it displays how long it takes the $eval and $apply methods to execute. On my machine it took an average of between 1-2 milliseconds.

I just expanded on zsong's code to add a check in the handler for the flag. If its true then just return because a click is already being handled. This prevents double clicks without worrying about angular timing or that sort of thing.

$scope.flag = false;
$scope.buttonClicked = function() {
    if ($scope.flag) {
        return;
    }
    $scope.flag = true;
    Service.doService.then(function(){
        //this is the callback for success
        $scope.flag = false;
    }).error(function(){
        //this is the callback for the error
        $scope.flag = false;
    })
}

As suggested, using ng-disabled will solve your problem. I made a plunker to illustrate it here.

to elaborate on @Jonathan Palumbo's answer (use ngDisabled) and @andre's question ("how to use that in a directive instead of controller?"): to allow the click or submission event to bubble up, you need to set the 'disabled' attribute to your clickable element (be it a button, a link, a span or a div) programmatically inside a timeout function (even with a delay of 0ms) which allows the event to be passed before its being disabled:

$timeout(function(){ elm.attr('disabled',true); }, 0);

I refer to @arun-p-johny's answer: prevent multiple form submissions using angular.js - disable form button.

I liked solution of user: zsong

But ng-dblclick="return false;" give a problem(I'm using Chrome Windows7) at js console you can see the error.

I can't comment (i don't have enough reputation to comment his solution)

Just use only ng-disabled.

As you can see at the plunker below if you have the two functions: ng-click and ng-dblclick And give a double click you will execute: 2 times click and 1 time dblclick

<bla ng-dblclick="count=count+1" ng-click="count=count+0.1" />

The double click gives you 1.2, so you can't prevent the 2 clicks with ng-dblclick, just add one more behavior when the second click happens.

Dblclick and click

Jonathan Palumbo gave an example with ng-disabled work at this thread.

As suggested in one of the answers, I tried using ng-dbliclick="return false;" which gives JS warning.


Insted I used ng-dblclick="return" which is working smooth. Though this only works inside the <form> tag.