prevent multiple form submissions using angular.js - disable form button

故事扮演 提交于 2019-11-28 18:54:09
Arun P Johny

Try a $timeout (the angularjs function)

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

I have a standard form and just use angular in the front-end, so if you just need to prevent a button being clicked twice while the server is responding then you can use this simple directive which is re-usable and requires no controller or ngModel.

http://plnkr.co/edit/2aZWQSLS8s6EhO5rKnRh?p=preview

app.directive('clickOnce', function($timeout) {
    return {
        restrict: 'A',
        link: function(scope, element, attrs) {
            var replacementText = attrs.clickOnce;

            element.bind('click', function() {
                $timeout(function() {
                    if (replacementText) {
                        element.html(replacementText);
                    }
                    element.attr('disabled', true);
                }, 0);
            });
        }
    };
});

It will disable the button and optionally change the text of the button. Use like so:

<button click-once>Button just disables</button>
<button click-once="Loading...">Text changes and button disables</button>

In its current form this will only work if you are doing standard form submissions and not ajax submission.

Just add a new property in your controller

$scope.processing = false;

In your method

$scope.processData = function(){
    $scope.processing = true;
    $http.post('').then(function(){
        $scope.processing = false;
    });
});

In your html bind ng-disabled attribute to the $scope.processing property to disable the button and show text while the method is processing.

Pascal Pixel Rigaux

Alternative (flexible & simple) solution (inspiration) : a wrapper function around the submit code that sets a scope variable. See live example.

Usage in controller:

$scope.submit = mutexAction($scope, 'sending', submit);

in view:

<form ng-submit="submit()">
  ...
  <button ng-disabled="sending">
    {{sending ? "Sending..." : "Send"}}
  </button>
</form>

The function (put it in a service):

function mutexAction(scope, semaphoreName, action) {
  return function() {
    if (scope[semaphoreName]) {
      // not queuing action, forget it!
      return;
    }
    scope[semaphoreName] = true;
    action()['finally'](function () {
      scope[semaphoreName] = false;
    });
  };
}
arctica

An addition to spenthil answer, a variant in coffee script + you can enable a button back if you need (e.g. when a form validation has failed and you want to try again)

class ClickOnceDirective

    constructor: (@$timeout) ->
        link = (scope, element, attrs) =>

            originalText = element.html()
            replacementText = attrs.clickOnce

            element.bind('click', =>
                @$timeout ->

                    if (replacementText)
                        element.html(replacementText)

                    element.attr('disabled', true)

                    # enable back
                    @$timeout ->
                         element.attr('disabled', false)
                        if (replacementText)
                             element.html(originalText)
                    , 500

                , 0)

         return {
         link
         restrict: 'A'
        }

directivesModule.directive 'clickOnce', ['$timeout', ClickOnceDirective]

I ended up going the directive route as well. The following is used instead of ng-click and expects the passed in function to return a promise (which restangular does). When the promise is resolved (response is returned) - it will allow subsequent submittals. Could also tweak this to add/remove ng-disabled.

// copied from ngClick and modified to provide single click only behavior
// expects function to return promise
app.directive('srMutexClick', function ($parse) {
  return {
    compile: function ($element, attr) {
      var fn = $parse(attr['srMutexClick']);
      return function srEventHandler(scope, element) {
        var submitting = false;
        element.on('click', function (event) {
          scope.$apply(function () {
            if (submitting) {
              return
            }
            submitting = true;
            // `submitting` is reset when promise is resolved
            fn(scope, {$event: event}).finally(function() { submitting = false });
          });
        });
      };
    }
  };
});

here is the simple way to do similar stuff with simple logical checks, text change can also be done in similar fashion.

<button type="submit" class="btn btn-success btn-lg btn-block"  ng-disabled="!userForm.$valid || isValid">Submit!</button>


 $scope.isValid = false;
    $scope.submitForm = function () {
        $scope.isValid = true;
        console.log('submit');
    }

The easiest and most common way of doing it is to rely on $submitted property of form object like so:

<form name="formName" ng-submit="submit()">
  ...
  <button type="submit" ng-disabled="formName.$submitted">Submit</button>
</form>

Here is a general way to do it for all AJAX requests using $http interceptors. If you have all of your REST routes starting from /api/ then:

     angular.module('yourapp').factory('loadingInterceptor',['$q','$rootScope',function($q,$rootScope) {
         var apiRe = /^\/api\//;
    return {
        request: function(config) {
            if (config.url.match(apiRe)) {
                $rootScope.loading = true;
            }
            config.headers = config.headers || {};

            return config;
        },
        response: function(res) {
            $rootScope.loading = false;
            return $q.resolve(res);
        },

        'responseError': function(rejection) {
            $rootScope.loading = false;
            return $q.reject(rejection);
        }
    };
}]);


angular.module('yourapp').config(['$httpProvider', function($httpProvider) {
    $httpProvider.interceptors.push('loadingInterceptor');
}]);

Using interceptor you won't have to put $scope.isLoading in each controller. The downside is that any button with ng-disabled="loading" will be blocked during request.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!