13

我想防止使用 angular.js 提交多个表单。这个问题与这个问题有关

当用户单击表单提交按钮时,提交按钮的值/标签应更改为“正在加载..”,按钮的状态将设置为禁用,并且应以正常方式触发提交事件,从而导致向服务器提交调用。这样用户会看到以下效果:

  1. 立即:提交按钮值更改为“正在加载..”并被禁用

  2. 一旦服务器响应:用户得到服务器请求的结果(而服务器响应是在没有角度干预的情况下处理的)

我创建了这个plunk来说明我的意思。我的问题与这一行有关:elm.attr('disabled',true);. 这不仅会禁用按钮,还会阻止传播提交事件。因此,我得到一个禁用的按钮(期望的结果),但没有提交表单(不期望的结果)。

如果您评论/取消评论这一行,您可以看到变化的行为:elm.attr('disabled',true);

知道如何改变这个吗?

4

9 回答 9

21

我有一个标准表单,只在前端使用角度,所以如果你只需要防止在服务器响应时点击两次按钮,那么你可以使用这个简单的指令,它是可重用的,不需要控制器或 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);
            });
        }
    };
});

它将禁用按钮并可选择更改按钮的文本。像这样使用:

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

在其当前形式中,这仅在您进行标准表单提交而不是 ajax 提交时才有效。

于 2013-11-07T00:12:53.753 回答
15

只需在控制器中添加一个新属性

$scope.processing = false;

在你的方法中

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

在您的 html 中将 ng-disabled 属性绑定到 $scope.processing 属性,以在方法处理时禁用按钮并显示文本。

于 2014-07-13T21:29:35.290 回答
11

尝试 $timeout(angularjs 函数)

$timeout(function(){
    elm.attr('disabled',true);
}, 0)
于 2013-04-04T09:45:27.677 回答
6

替代(灵活和简单)解决方案(灵感):围绕提交代码设置范围变量的包装函数。见活的例子

在控制器中的用法:

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

鉴于:

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

功能(将其放入服务中):

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;
    });
  };
}
于 2014-07-13T20:11:24.497 回答
2

除了spedhil答案之外,咖啡脚本中的一个变体 + 如果需要,您可以启用一个返回按钮(例如,当表单验证失败并且您想重试时)

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]
于 2014-12-02T12:05:32.793 回答
2

最简单和最常见的方法是依赖$submitted表单对象的属性,如下所示:

<form name="formName" ng-submit="submit()">
  ...
  <button type="submit" ng-disabled="formName.$submitted">Submit</button>
</form>
于 2017-01-07T12:52:28.233 回答
1

我最终也选择了指令路线。以下用于代替 ng-click 并期望传入的函数返回一个承诺(restangular 会这样做)。当承诺被解决(响应被返回) - 它将允许后续提交。也可以调整它以添加/删除 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 });
          });
        });
      };
    }
  };
});
于 2014-06-24T17:45:24.063 回答
1

这是通过简单的逻辑检查来做类似事情的简单方法,文本更改也可以以类似的方式完成。

<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');
    }
于 2015-12-22T17:46:57.390 回答
0

这是使用 $http 拦截器对所有 AJAX 请求执行此操作的通用方法。如果您的所有 REST 路由都从 /api/ 开始,那么:

     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');
}]);

使用拦截器,您不必将 $scope.isLoading 放在每个控制器中。缺点是任何带有 ng-disabled="loading" 的按钮都会在请求期间被阻止。

于 2016-11-29T15:58:29.000 回答