工作H5开发需要做短信验证码及秒倒计时,如果是用纯JS做好像还比较容易,但用angularJS做还是一些坑,特此记录一下,有如下几种实现方式。
一.setTimeout方式实现
<html> <head> <title>AngularJs倒计时</title> </head> <body> <div ng-controller="registerCtrl"> <div> <div> <label>验证码:</label> <input ng-model='verifCode' ng-disabled="verifCodeDisabled" placeholder="请输入验证码"></input> <button ng-bind="timing" ng-click="reacQuire()"></button> </div> </div> <div class="btn-group"> <button ng-click="submitBtn()" ng-disabled="submitBtnDisabled">提交</button> </div> </div> <script src="jquery-1.8.3.js"></script> <script src="angular1.2.9.js"></script> <script src="index.js"></script> </body> </html>
index.js
var app = angular.module('myModule',[]); app.controller('registerCtrl',function($scope,$interval){ let countDown = 10; $scope.timing = countDown + "s"; $scope.verifCode = ''; $scope.verifCodeDisabled = false; $scope.submitBtnDisabled = false; function settime() { if(countDown > 0) { setTimeout(function() {settime(countDown--); $scope.$apply();},1000); $scope.timing = countDown + 's'; }else { $scope.timing = "重新获取"; $scope.verifCodeDisabled = true; $scope.submitBtnDisabled = true; } } settime(); $scope.reacQuire = function() { if(countDown <= 0) { countDown = 10; $scope.timing = countDown + "s"; settime(); $scope.verifCodeDisabled = false; $scope.submitBtnDisabled = false; } }; $scope.submitBtn = function() { if(!$scope.verifCode) { alert('请输入验证码'); return; } alert('提交成功'); }; }); angular.element(document).ready(function() { angular.bootstrap(document,['myModule']); });
运行效果:
当倒计到0,输入框和“提交”按钮变灰,倒计时按钮变成“重新获取”,如下所示:
在这里的实现在,必须注意settime函数中的$scope.$apply();在这里,如果没有$scope.$apply();则倒计时,只会倒计一次,虽然JS确实在一秒一秒的减少,但由于异步(延迟)的存在,当开始执行回调函数的时候,angularJS自身controller中的脏值检测已经结束,无法检测到回调函数导致数据的变化,导致页面无法正常倒计时间。同样,如果在settime函数级别加上$scope.$apply();效果确实可以,但F12可以看到angularJS报错$apply already in progress。详见:理解和解决angularJS报错$apply already in progress。
二.修改网上查看的实现方式,也能达到目的
<html> <head> <title>AngularJs倒计时</title> </head> <body> <div ng-controller="registerCtrl"> <a href="javascript:" ng-click="sendphonecode(reg_phone)" ng-class="paraclass" ng-bind="paracont">获取验证码</a> </div> <script src="jquery-1.8.3.js"></script> <script src="angular1.2.9.js"></script> <script src="app.js"></script> </body> </html>
app.js
var app = angular.module('myModule',$interval) { $scope.paracont = "获取验证码"; $scope.paraclass = "but_null"; $scope.paraevent = true; var second = 10,timePromise = undefined; function interval(){ if(second<=0){ $interval.cancel(timePromise); timePromise = undefined; $scope.paracont = "重发验证码"; $scope.paraclass = "but_null"; $scope.paraevent = true; }else{ $scope.paracont = second + "秒后可重发"; $scope.paraclass = "not but_null"; second--; } } timePromise = $interval(interval,1000,100); //表示每一秒执行一次,执行100次 $scope.sendphonecode = function() { if(second <= 0) { second = 10; timePromise = $interval(interval,100); //表示每一秒执行一次,执行100次 } }; }); angular.element(document).ready(function() { angular.bootstrap(document,['myModule']); });
运行效果:
三.用$interval实现
<html> <head> <title>AngularJs倒计时</title> </head> <body> <div ng-controller="registerCtrl"> <div> <div> <label>验证码:</label> <input ng-model='verifCode' ng-disabled="verifCodeDisabled" placeholder="请输入验证码"></input> <button ng-bind="timing" ng-click="reacQuire()"></button> </div> </div> <div class="btn-group"> <button ng-click="submitBtn()" ng-disabled="submitBtnDisabled">提交</button> </div> </div> <script src="jquery-1.8.3.js"></script> <script src="angular1.2.9.js"></script> <script src="index02.js"></script> </body> </html>
index02.js
var app = angular.module('myModule',$interval){ let countDown = 10; $scope.timing = countDown + "s"; $scope.verifCode = ''; $scope.verifCodeDisabled = false; $scope.submitBtnDisabled = false; var time = $interval(interval,1000); function interval() { countDown--; $scope.timing = countDown + "s"; if(countDown <= 0) { $interval.cancel(time); $scope.timing = "重新获取"; $scope.verifCodeDisabled = true; $scope.submitBtnDisabled = true; } } $scope.reacQuire = function() { if(countDown <= 0) { countDown = 10; $scope.timing = countDown + "s"; time = $interval(interval,1000); $scope.verifCodeDisabled = false; $scope.submitBtnDisabled = false; } }; $scope.submitBtn = function() { if(!$scope.verifCode) { alert('请输入验证码'); return; } alert('提交成功'); }; }); angular.element(document).ready(function() { angular.bootstrap(document,['myModule']); });
四.指令方式
<html> <head> <title>AngularJs倒计时</title> </head> <body> <div ng-controller="registerCtrl"> <timer-button show-timer="initTime" timeout="timeoutValue">获取验证码</timer-button> </div> <script src="jquery-1.8.3.js"></script> <script src="angular1.2.9.js"></script> <script src="index03.js"></script> </body> </html>
index03.js
var app = angular.module('myModule',$interval){ $scope.initTime = false; $scope.timeoutValue = 10000; }); app.directive('timerButton',function($timeout,$interval){ return { restrict: 'AE',scope: { showTimer: '=',timeout: '=' },link: function(scope,element,attrs){ scope.timer = false; scope.timerCount = scope.timeout / 1000; scope.text = "获取验证码"; scope.onClick = function(){ scope.showTimer = true; scope.timer = true; scope.text = 's'; var counter = $interval(function(){ if(scope.timerCount > 0) { scope.timerCount = scope.timerCount - 1; }else { scope.timerCount = ''; } },1000); $timeout(function(){ scope.text = "重新获取"; scope.timer = false; $interval.cancel(counter); scope.showTimer = false; scope.timerCount = scope.timeout / 1000; },scope.timeout); } },template: '<button ng-click="onClick()" class="button button-calm xgmm-btn" ng-disabled="timer"><span ng-show="showTimer">{{ timerCount }}</span>{{text}}</button>' }; }); angular.element(document).ready(function() { angular.bootstrap(document,['myModule']); });
运行效果:
点击”获取验证码“按钮,进行秒倒计时
倒计到0时,显示”重新获取“,此时,可点击“重新获取”按钮重新进行倒计时。
五.AngularJs $interval 和 $timeout
1.$interval
window.setInterval的Angular包装形式。Fn是每次延迟时间后被执行的函数。
间隔函数的返回值是一个承诺。这个承诺将在每个间隔刻度被通知,并且到达规定迭代次数后被取消,如果迭代次数未定义,则无限制的执行。通知的值将是运行的迭代次数。取消一个间隔,调用$intreval.cancel(promise)。
备注:当你执行完这项服务后应该把它销毁。特别是当controller或者directive元素被销毁时而$interval未被销毁。你应该考虑到在适当的时候取消interval事件。
使用:$interval(fn,delay,[count],[invokeApply],[Pass]);
fn:一个将被反复执行的函数。
delay:每次调用的间隔毫秒数值。
count:循环次数的数值,如果没设置,则无限制循环。
invokeApply:如果设置为false,则避开脏值检查,否则将调用$apply。
Pass:函数的附加参数。
方法:cancel(promise);
取消与承诺相关联的任务。
promise:$interval函数的返回值。
使用代码:
(function () { angular.module("Demo",[]) .controller("testCtrl",["$interval",testCtrl]); function testCtrl($interval){ var toDo = function () { console.log("Hello World"); }; $interval(toDo,3000,10); }; }());
2.$timeout
window.setTimeout的Angular包装形式。Fn函数包装成一个try/catch块,代表$exceptionHandler服务里的任何异常。
timeout函数的返回值是一个promise,当到达设置的超时时间时,这个承诺将被解决,并执行timeout函数。
需要取消timeout,需要调用$timeout.cancel(promise);
使用: $timeout(fn,[delay],[invokeApply]);
fn:一个将被延迟执行的函数。
delay:延迟的时间(毫秒)。
invokeApply:如果设置为false,则跳过脏值检测,否则将调用$apply。
方法:cancel(promise);
取消与承诺相关联的任务。这个的结果是,承诺将被以摒弃方式来解决。
promise:$timeout函数返回的承诺。
使用代码:
(function () { angular.module("Demo",["$timeout",testCtrl]); function testCtrl($timeout){ var toDo = function () { console.log("Hello World"); }; $timeout(toDo,5000) }; }());
大致使用方法可以和原生js的setInterval和setTimeout那样使用,一些使用小技巧可以用在浏览器单线程的事件执行方面。