Prevent Double or Multiple Click in Angular 1.x

Faiz Mohamed Haneef's Picture
February 17, 2017

Angular has built in directive for click and doubleclick namely ng-click and ng-dbclick respectively. However, it is difficult to maintain the state of underlying element based on an async operation.

Below, I have created a directive, which affects the parent scope and attaches each target button element(see oneClickOnly[attr.ocoId]).

The directive code is below

angular.module('app').directive('oneClickOnly', [  
  '$parse', '$compile', function($parse, $compile) {
    return {
      restrict: 'A',
      scope: false, // get the parent scope directly
      compile: function(element, attr) {
        var fn = $parse(attr['oneClickOnly']);
        return {
          pre: function(scope, element, attr) {
            scope.oneClickOnly = scope.oneClickOnly || {}
            scope.oneClickOnly[attr.ocoId] = scope.oneClickOnly[attr.ocoId] || {}
            scope.oneClickOnly[attr.ocoId].fn = function(value) {
              scope.oneClickOnly[attr.ocoId].val = value
              if(value) {                
                element.text(attr.ocoTextWait)
                element.attr('disabled', true)
              } else {
                element.text(attr.ocoText)
                element.attr('disabled', false)
              }
            }
            var clickingCallback = function(e) {
              if(scope.oneClickOnly 
                && scope.oneClickOnly[attr.ocoId]
                && scope.oneClickOnly[attr.ocoId].val) {
                return
              }              
              scope.oneClickOnly[attr.ocoId].fn(true)
              fn(scope, { $event: e });
            };
            element.bind("click", clickingCallback)
          }
        }
      }
    }
  }
])

The html would change as below
note - ng-click is not required

<md-button class="md-raised md-primary" oco-id="addCenter"  
  oco-text="Save" oco-text-wait="Saving" 
  one-click-only="doAsyncProcess()">

The controller would be

onCompleteOfAsyncProcess() {  
  // addCenter should be same as oco-id
  $scope.oneClickOnly['addCenter'].fn(false)
}