我想使用angular.js阻止多个表单提交。问题与此question有关。
当用户点击表单提交按钮时,提交按钮的值/标签应更改为“正在加载...”,按钮的状态将设置为禁用,并且应以正常方式触发提交事件,导致对服务器的提交调用。这样用户就会看到以下效果:
立即:提交按钮值更改为“正在加载...”并被禁用
一旦服务器响应:用户会看到服务器请求的结果(而服务器响应的处理没有角度干预)
我创建了这个plunk以显示我的意思。我的问题涉及这一行:elm.attr('disabled',true);
。这不仅会禁用该按钮,还会阻止传播提交事件。因此,我得到一个禁用按钮(所需结果),但表单未提交(不良结果)。
如果您评论/取消注释此行,则可以看到更改的行为:elm.attr('disabled',true);
知道如何改变这个吗?
答案 0 :(得分:21)
我有一个标准的表单,只需在前端使用angular,所以如果你只是需要防止在服务器响应时点击两次按钮,那么你可以使用这个可重复使用的简单指令,并且不需要controller或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时才会有效。
答案 1 :(得分:15)
只需在控制器中添加新属性
即可$scope.processing = false;
在你的方法中
$scope.processData = function(){
$scope.processing = true;
$http.post('').then(function(){
$scope.processing = false;
});
});
在$ scope.-disabled属性的html bind ng-disabled属性中,禁用按钮并在方法处理时显示文本。
答案 2 :(得分:11)
尝试$ timeout(angularjs函数)
$timeout(function(){
elm.attr('disabled',true);
}, 0)
答案 3 :(得分:6)
替代(灵活和简单)解决方案(inspiration):围绕提交代码的包装函数,用于设置范围变量。请参阅live example。
控制器中的用法:
$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;
});
};
}
答案 4 :(得分:2)
spenthil答案的补充,咖啡脚本中的变体+您可以根据需要启用按钮(例如,当表单验证失败并且您想再次尝试时)
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]
答案 5 :(得分:1)
我最终也走了指令路线。使用以下代替ng-click并期望传入的函数返回一个promise(这是一个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 });
});
});
};
}
};
});
答案 6 :(得分: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');
}
答案 7 :(得分:1)
最简单和最常见的方法是依赖于表单对象的$submitted
属性,如下所示:
<form name="formName" ng-submit="submit()">
...
<button type="submit" ng-disabled="formName.$submitted">Submit</button>
</form>
答案 8 :(得分:0)
这是使用$ http拦截器为所有AJAX请求执行此操作的一般方法。 如果您拥有从/ api / then开始的所有REST路由:
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”的按钮。