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:39

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.

查看更多
做个烂人
3楼-- · 2019-01-22 09:45

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.

查看更多
Luminary・发光体
4楼-- · 2019-01-22 09:45

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.

查看更多
登录 后发表回答