Simple-Solution
Simple-Solution

Reputation: 4289

Prevent multiple ajax requests onclick

I've an issue with multiple ajax requests. For example I've a form with a button, and onclick it runs a service which essentially load list of items in a table; for now it should load a single item into a table when I hit the button.

However, when I hit the button multiple times, the same item is duplicated when its loaded.

How can I prevent while there is still no callback from the first one?

current ng service

var getItems = function () {

    var def = $q.defer();

    Items.get().then(function (items) {
       def.resolve(items);
    }, function (err) {
       ...
    });
};

Not sure if this is a solution, but when I write above code like this:

var def = false;
var getItems = function () {

    def = $q.defer();

    Items.get().then(function (items) {
       def.resolve(items);
    }, function (err) {
       ...
    });
};

This stops the duplication when I initialize the def = false, not sure if this is the correct approach by resetting the previous/old request to false?

Upvotes: 0

Views: 5250

Answers (3)

harishr
harishr

Reputation: 18065

you can create a reusable directive so that on any button which is clickable, it doesnt get pressed twice

app.directive('clickAndDisable', function() {
  return {
    scope: {
      clickAndDisable: '&'
    },
    link: function(scope, iElement, iAttrs) {
      iElement.bind('click', function() {
        iElement.prop('disabled',true);
        scope.clickAndDisable().finally(function() {
          iElement.prop('disabled',false);
        })
      });
    }
  };
});

This can be used on a button as follows:

<button click-and-disable="functionThatReturnsPromise()">Click me</button>

Upvotes: 0

SoluableNonagon
SoluableNonagon

Reputation: 11755

You can put a lock on the function to prevent the code from running multiple times at once or at all:

// your service
$scope.isRunning = false;

var getItems = function () {

    if(!$scope.isRunning){
        $scope.isRunning = true;
        var def = $q.defer();

        Items.get().then(function (items) {
           def.resolve(items);
        }, function (err) {
           ...
        }).finally(function(){
             //$scope.isRunning = false; // once done, reset isRunning to allow to run again. If you want it to run just once then exclude this line
        });
    }
};

Unsure how you want to handle the button in terms of being clicked multiple times

You can hide it on click:

<button ng-hide="isRunning">Stuff</button>

You can disable it on click:

<button ng-disabled="isRunning">Stuff</button>

if disabling, you should probably give feedback like changing opacity:

<button ng-disabled="isRunning" ng-class='{"opacity-half": isRunning}'>Stuff</button>
.opacity-half { opacity: 0.5 }

Upvotes: 3

Onur Topal
Onur Topal

Reputation: 3061

the below code should do the trick I am avoiding some angular specific syntax hope that helps;

function yourContoller(/*all injectables*/) {
  var requesting = false;
  $scope.buttonClick = function() {
     if (!requesting) {
       requesting = true;
       yourService.getItems().then(function(response) {
          /*your code to handle response*/
          requesting = false;
       });
     }
  };
}

if you want to disable a button in the view you can expose this variable by simply using scope ($scope.requesting = false;) with ng-disabled.

Upvotes: 2

Related Questions