Angularjs 防止/处理角度传感器中的双击

Angularjs 防止/处理角度传感器中的双击,angularjs,Angularjs,在angular中,我们可以设置一个按钮来发送ajax请求,如下所示: ... ng-click="button-click" 在控制器中: ... $scope.buttonClicked = function() { ... ... // make ajax request ... ... } 因此,为了防止双重提交,我可以在单击按钮时将标志设置为buttonclicked=true,并在ajax回调完成时将其取消设置。但是,即使这样,控制权也会被处理回a

在angular中,我们可以设置一个按钮来发送ajax请求,如下所示:

... ng-click="button-click"
在控制器中:

...
$scope.buttonClicked = function() {
   ...
   ...
   // make ajax request 
   ...
   ...
}
因此,为了防止双重提交,我可以在单击按钮时将标志设置为buttonclicked=true,并在ajax回调完成时将其取消设置。但是,即使这样,控制权也会被处理回angular,后者将更新Dom。这意味着有一个小窗口,在原来的按钮点击完全100%完成之前,可以再次点击按钮

这是一个小窗口,但仍然可以发生。完全避免这种情况发生的任何提示-客户端,即不对服务器进行任何更新

谢谢

首先,您最好添加,当它检测到双击时,只需返回
false

<ANY ng-click="buttonClicked()" ng-dblclick="return false">

当ajax调用成功或失败时,您需要处理这两种情况,因为如果调用失败,您不希望它显示为已禁用以混淆用户。

使用
ng disabled
在这方面效果很好。无论我多么疯狂地点击控制台,消息只填充了一次

var-app=angular.module('plunker',[]);
应用程序控制器('MainCtrl',函数($scope){
$scope.submitData=函数(){
$scope.buttonDisabled=true;
控制台日志(“点击按钮”);
}
函数扩充(){
变量名称,fn;
for(名称在$scope中){
fn=$scope[name];
如果(fn的类型==‘函数’){
if(name.indexOf($)!=-1){
$scope[name]=(函数(名称,fn){
var args=参数;
返回函数(){
console.log(“调用”+名称);
控制台。时间(名称);
fn.应用(此,参数);
console.timeEnd(名称);
}
})(姓名,fn);
}
}
}
}
增强();
});

安古拉斯普朗克

按照建议,使用
ng disabled
将解决您的问题。我制作了一个plunker来说明这一点。

我刚刚扩展了zsong的代码,在处理程序中为标志添加了一个检查。如果为true,则返回,因为单击已被处理。这样可以防止双击,而不用担心角度计时之类的问题

$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;
    })
}

要详细说明@Jonathan Palumbo的答案(使用ngDisabled)和@andre的问题(“如何在指令中而不是控制器中使用它?”):要允许单击或提交事件冒泡,需要在超时函数中以编程方式将“disabled”属性设置为可单击元素(可以是按钮、链接、span或div)(即使延迟0毫秒)允许在禁用事件之前传递事件:

$timeout(function(){ elm.attr('disabled',true); }, 0);

我参考了@arun-p-johny的答案:。

我喜欢用户的解决方案:zsong

但是ng dblclick=“return false;”在js控制台上给出一个问题(我使用的是Chrome Windows7),您可以看到错误

我不能评论(我没有足够的声誉评论他的解决方案)

仅使用禁用的ng

如下图所示,如果您有两个功能: ng单击并ng dblclick 然后双击,您将执行: 2次单击和1次dblclick

<bla ng-dblclick="count=count+1" ng-click="count=count+0.1" />

双击为您提供了1.2,因此您无法使用ng dblclick阻止两次单击,只需在第二次单击时再添加一个行为即可


Jonathan Palumbo给出了一个在这个线程中使用ng禁用工作的示例。

正如其中一个答案中所建议的,我尝试使用
ng dbliclick=“return false;”“
来发出JS警告



相反,我使用了运行平稳的
ng dblclick=“return”
。尽管这只在
标记中起作用。

再次在zsong的doe上展开:

首先,使用此解决方案,人们可以使用双击来使用你的应用程序。老年人有时会这样做(因为他们习惯于双击在windows上打开程序),其他人也会错误地这样做


其次,用户可以尽快单击,他们的浏览器将等待服务器响应,然后再重新启用按钮(这是对Mark Rajcok在zsong的帖子中的评论的修正:“如果AJAX请求花费的时间超过浏览器的双击时间/窗口,这将不起作用。即,用户可以暂停并再次单击。”)

在html中


我最近不得不这么做,我带来了一些解决方案。对我来说,这是一个指令,它是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
        };
    }
])
这是我的测试(以防有人感兴趣)

“严格使用”;
描述(“一键式按钮指令”,函数(){
var$scope,testButton,$compile,clickedEvent;
var计数器=0;
beforeach(函数(){
计数器=0;
模块('shared.form.validation');
注入(函数($rootScope,$compile){
$compile=\$compile;
$scope=$rootScope.$new();
$scope.clickEvent=函数(事件){
计数器++;
};
});
});
它(“防止按钮被多次单击”,函数(){
var html=“测试按钮”;
testButton=$compile(html)($scope);
$scope.$digest();
testButton.click();
expect(函数(){testButton.click();}).toThrow(“已单击”);
期望值(计数器)。toBe(1);
});
它(“不允许ng单击同一标记”,函数(){
var html=“测试按钮”;
expect(function(){$compile(html)($scope);}).toThrow(“不能在一个元素上同时单击ng和一次单击”);
});
它(“适用于同一范围内的多个按钮”),函数(){
var=2=0;
$scope.clickEvent2=函数(事件){
计数器2++;
};
var html=“测试按钮”;
var html2=“测试按钮”;
testButton=$compile(html)($scope);
var testButton2=$compile(html2)($scope);
$scope.$digest();
testButton.click();
expect(函数(){testButton2.click();}).not.toThrow(“Alre
<bla ng-dblclick="count=count+1" ng-click="count=count+0.1" />
<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 --;
    })
}
.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);
});
});
npm install angular-click-and-wait
bower install angular-click-and-wait
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>
ng-mousedown="$event.preventDefault();"
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();
            }
        }
    });