在角度,我们可以设置一个按钮,在视图中发送像这样的ajax请求:
... ng-click="button-click"
并在控制器中:
...
$scope.buttonClicked = function() {
...
...
// make ajax request
...
...
}
因此,为了防止双重提交,我可以在单击按钮时将buttonclicked = true设置为true,并在ajax回调完成时取消设置。但是,即使这样,控制也会被处理回角色,谁将更新到Dom。这意味着在原始按钮单击完全100%完成之前,有一个小窗口可以再次单击该按钮。
这是一个小窗口,但仍然可以发生。完全避免这种情况发生的任何提示 - 客户端,即不对服务器进行任何更新。
由于
答案 0 :(得分:39)
首先,您最好添加ngDblclick,当它检测到双击时只返回false
:
<ANY ng-click="buttonClicked()" ng-dblclick="return false">
如果您想等待Ajax调用完成,则可以通过设置禁用ng来禁用该按钮
<ANY ng-click="buttonClicked()" ng-dblclick="return false;" ng-disabled="flag">
在你的控制器中,你可以做到
$scope.flag = false;
$scope.buttonClicked = function() {
$scope.flag = true;
Service.doService.then(function(){
//this is the callback for success
$scope.flag = false;
}).error(function(){
//this is the callback for the error
$scope.flag = false;
})
}
当ajax调用成功或失败时,您需要同时处理这两种情况,因为如果它失败了,您不希望它显示为带有混乱的用户。
答案 1 :(得分:33)
使用ng-disabled
在example中运行得很好。无论我多么激烈地点击控制台消息只填充一次。
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.submitData = function() {
$scope.buttonDisabled = true;
console.log("button clicked");
}
function augment() {
var name, fn;
for (name in $scope) {
fn = $scope[name];
if (typeof fn === 'function') {
if (name.indexOf("$") !== -1) {
$scope[name] = (function(name, fn) {
var args = arguments;
return function() {
console.log("calling " + name);
console.time(name);
fn.apply(this, arguments);
console.timeEnd(name);
}
})(name, fn);
}
}
}
}
augment();
});
<!doctype html>
<html ng-app="plunker">
<head>
<meta charset="utf-8">
<title>AngularJS Plunker</title>
<link rel="stylesheet" href="style.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<input type="button" ng-click="submitData()" ng-disabled="buttonDisabled" value="Submit" />
</body>
</html>
我很好奇将角度应用于buttonDisabled
标志需要多长时间。如果您在plunker example中检查控制台,则会显示执行$eval
和$apply
方法所需的时间。在我的机器上,它平均需要1-2毫秒。
答案 2 :(得分:11)
我只是扩展了zsong的代码,在标志的处理程序中添加一个检查。如果它是真的那么只返回因为已经处理了一个点击。这可以防止双击而不用担心角度定时或类似的事情。
$scope.flag = false;
$scope.buttonClicked = function() {
if ($scope.flag) {
return;
}
$scope.flag = true;
Service.doService.then(function(){
//this is the callback for success
$scope.flag = false;
}).error(function(){
//this is the callback for the error
$scope.flag = false;
})
}
答案 3 :(得分:6)
您可以使用我刚刚完成的指令,以防止用户在执行异步操作时多次单击元素
https://github.com/mattiascaricato/angular-click-and-wait
您可以使用 npm 或 bower
将其添加到项目中npm install angular-click-and-wait
或
bower install angular-click-and-wait
const myApp = angular.module('myApp', ['clickAndWait']);
myApp.controller('myCtrl', ($scope, $timeout) => {
$scope.asyncAction = () => {
// The following code simulates an async action
return $timeout(() => angular.noop, 3000);
}
});
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<script src="https://cdn.rawgit.com/mattiascaricato/angular-click-and-wait/master/dist/click-and-wait.js"></script>
<section ng-app="myApp">
<div ng-controller="myCtrl">
<button click-and-wait="asyncAction()">Click Me!</button>
</div>
</section>
&#13;
注意:作为参数传递的异步操作应该是Promise
答案 4 :(得分:3)
我喜欢用户的解决方案:zsong
但是ng-dblclick =&#34;返回false;&#34;在js控制台上出现问题(我使用的是Chrome Windows7),您可以看到错误。
我无法评论(我没有足够的声誉评论他的解决方案)
只使用ng-disabled。
正如您在下面的plunker中看到的那样,如果您有两个功能: ng-click和ng-dblclick 然后双击你将执行: 2次点击和1次dblclick
<bla ng-dblclick="count=count+1" ng-click="count=count+0.1" />
双击为您提供1.2,因此您无法使用ng-dblclick阻止2次点击,只需在第二次点击时再添加一个行为。
Jonathan Palumbo在这个帖子中举了一个关于ng-disabled工作的例子。
答案 5 :(得分:2)
我最近不得不这样做,而且我已经把几个解决方案放在了一起。这对我有用,它是一个替代ng-click的指令,只能点击一次。
此解决方案会引发错误,这使得测试非常容易。
.directive('oneClickOnly', [
'$parse', '$compile', function($parse, $compile) {
return {
restrict: 'A',
compile: function(tElement, tAttrs) {
if (tAttrs.ngClick)
throw "Cannot have both ng-click and one-click-only on an element";
tElement.attr('ng-click', 'oneClick($event)');
tElement.attr('ng-dblclick', 'dblClickStopper($event)');
tElement.removeAttr('one-click-only');
var fn = $parse(tAttrs['oneClickOnly']);
return {
pre: function(scope, iElement, iAttrs, controller) {
console.log(scope, controller);
var run = false;
scope.oneClick = function(event) {
if (run) {
throw "Already clicked";
}
run = true;
$(event.toElement).attr('disabled', 'disabled');
fn(scope, { $event: event });
return true;
};
scope.dblClickStopper = function(event) {
event.preventDefault();
throw "Double click not allowed!";
return false;
};
$compile(iElement)(scope);
}
};
},
scope: true
};
}
])
以下是我的测试(如果有人感兴趣的话)
'use strict';
describe("The One click button directive", function() {
var $scope, testButton, $compile, clickedEvent;
var counter = 0;
beforeEach(function () {
counter = 0;
module('shared.form.validation');
inject(function ($rootScope, _$compile_) {
$compile = _$compile_;
$scope = $rootScope.$new();
$scope.clickEvent = function (event) {
counter++;
};
});
});
it("prevents a button from being clicked multiple times", function () {
var html = "<a one-click-only='clickEvent()'>test button</a>";
testButton = $compile(html)($scope);
$scope.$digest();
testButton.click();
expect(function () { testButton.click(); }).toThrow("Already clicked");
expect(counter).toBe(1);
});
it("doesn't allow ng-click on the same tag", function() {
var html = "<a ng-click='clickEvent()' one-click-only='clickEvent()'>test button</a>";
expect(function () { $compile(html)($scope); }).toThrow("Cannot have both ng-click and one-click-only on an element");
});
it("works for multiple buttons on the same scope", function () {
var counter2 = 0;
$scope.clickEvent2 = function (event) {
counter2++;
};
var html = "<a one-click-only='clickEvent()'>test button</a>";
var html2 = "<a one-click-only='clickEvent2()'>test button</a>";
testButton = $compile(html)($scope);
var testButton2 = $compile(html2)($scope);
$scope.$digest();
testButton.click();
expect(function () { testButton2.click(); }).not.toThrow("Already clicked");
expect(counter).toBe(1);
expect(counter2).toBe(1);
});
});
答案 6 :(得分:1)
根据建议,使用ng-disabled
可以解决您的问题。我做了一个插图来说明它here。
答案 7 :(得分:1)
详细说明@Jonathan Palumbo的回答(使用ngDisabled)和@andre的问题(&#34;如何在指令中使用它而不是控制器?&#34;):允许点击或提交事件要冒泡,你需要设置“禁用”&#39;在超时函数内以编程方式(即使延迟为0毫秒)将属性指向可点击元素(无论是按钮,链接,跨度还是div),这允许事件在被禁用之前传递:
$timeout(function(){ elm.attr('disabled',true); }, 0);
我指的是@ arun-p-johny的回答:prevent multiple form submissions using angular.js - disable form button。
答案 8 :(得分:1)
您可以创建指令以防止双击:
angular.module('demo').directive('disableDoubleClick', function ($timeout) {
return {
restrict: 'A',
link: function (scope, elem, attrs) {
elem.bind('click', function(){
$timeout(function(){
elem.attr('disabled','disabled');
}, 20);
$timeout(function(){
elem.removeAttr('disabled');
}, 500);
});
}
};
});
你可以在任何可点击的项目上使用它,如下所示:
<button ng-click="clickMe()" disable-double-click >Click Me</button>
答案 9 :(得分:0)
正如其中一个答案中所建议的那样,我尝试使用ng-dbliclick="return false;"
来提供JS警告。
Insted我使用了ng-dblclick="return"
,它工作顺利。虽然这只适用于<form>
标记。
答案 10 :(得分:0)
再次扩展zsong的母鹿:
首先,通过此解决方案,人们可以使用双击来使用您的应用。老人有时这样做(因为他们习惯于双击以在Windows上打开程序),而其他人也是这样做的。
其次,用户可以尽快点击,他们的浏览器会在重新启用按钮之前等待服务器响应(这是对Mark Rajcok对zsong帖子的评论的解决方法:“如果AJAX请求花费的时间比浏览器的双击时间/窗口长,那么这将无效。即,用户可以暂停并再次单击。“)。
你的HTML中的
<ANY
ng-click="buttonClicked();
submitButtonDisabled = 1 + submitButtonDisabled;"
ng-disabled="submitButtonDisabled > 0;"
ng-init="submitButtonDisabled = 0;"
>
控制器中的
$scope.buttonClicked = function() {
Service.doService.then(function(){
//this is the callback for success
// you probably do not want to renable the button here : the user has already sent the form once, that's it - but just if you want to :
$scope.submitButtonDisabled --;
//display a thank you message to the user instead
//...
}).error(function(){
//this is the callback for the error
$scope.submitButtonDisabled --;
})
}
答案 11 :(得分:0)
我发现一个简单的解决方案,我认为这比其他答案更好的是防止mousedown事件上的浏览器默认行为。
ng-mousedown="$event.preventDefault();"
它不会阻止点击事件,但会阻止双击事件:)
答案 12 :(得分:0)
You can handle the form validation
$('form.your-form').validate({
rules: {
name: 'required',
email: {
required: true,
email: true
}
},
submitHandler: function (form) {
// Prevent double submission
if (!this.beenSubmitted) {
this.beenSubmitted = true;
form.submit();
}
}
});