prevent multiple form submissions using angular.js

2019-01-22 09:11发布

I want to prevent multiple form submissions using angular.js. The question is related to this question.

When the user clicks on a form submit button the value / label of the submit button should change to "loading..", status of the button will be set to disabled AND the submit event should be triggered in the normal way, leading to a submit call to the server. This way the user would see the following effect:

  1. Immediately: The submit button value changes to "loading.." and gets disabled

  2. As soon as the server responds: user gets presented the result of the server request (whereas server responds are handled without angular intervention)

I created this plunk to show what I mean. My issue relates to this line: elm.attr('disabled',true); . This does not only disable the button, but also prevent to propagate the submit event. Thus I get a disabled button (desired result), but the form does not get submitted (undesired result).

You can see the changing behavior if you comment / uncomment this line : elm.attr('disabled',true);

Any idea how to change this?

9条回答
劳资没心,怎么记你
2楼-- · 2019-01-22 09:19

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 });
          });
        });
      };
    }
  };
});
查看更多
ら.Afraid
3楼-- · 2019-01-22 09:20

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>
查看更多
老娘就宠你
4楼-- · 2019-01-22 09:29

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;
    });
  };
}
查看更多
闹够了就滚
5楼-- · 2019-01-22 09:31

Try a $timeout (the angularjs function)

$timeout(function(){
    elm.attr('disabled',true);
}, 0)
查看更多
疯言疯语
6楼-- · 2019-01-22 09:36

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]
查看更多
何必那么认真
7楼-- · 2019-01-22 09:38

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');
    }
查看更多
登录 后发表回答