如何使用 AngularJS 显示每秒倒计时的消息



我有用来检查与服务器连接的代码。我的代码每 60 秒运行一次。代码运行后,它会创建一条消息,并显示在页面上。这是我到目前为止所拥有的:

检查以下内容的代码:

$interval(function () {
        us.isConnected().then(closeConnect, openConnect);
    }, 60 * 1000);

执行检查的代码

isConnected = (): ng.IPromise<any> => {
    var self = this;
    var deferred = this.$q.defer();
    this.$http({
        method: 'GET',
        url: self.ac.baseUrl + '/api/Connect/Verify'
    })
        .success(() => {
            self.connects = 0;
            self.connectMessage = null;
            deferred.resolve();
        })
        .error(() => {  
            if (self.connects == 0) {
                self.connectMessage = "Unable to establish a connection to the server. " + retryMessage();
            } else if (self.connects == 1) {
                self.connectMessage = "Unable to establish a connection to the server for " + self.connects + " minute" + retryMessage();
            } else {
                self.connectMessage = "Unable to establish a connection to the server for " + self.connects + " minutes." + retryMessage();
            }
            self.connects++; 
            deferred.reject();
        });
    return deferred.promise;
};

我想做的是有一个名为retryMessage()的简单函数,它将允许我给出这样的消息:

 Unable to establish a connection to the server for 164 minutes. 
 Connection will be retried in 59 seconds.
 Unable to establish a connection to the server for 164 minutes. 
 Connection will be retried in 58 seconds.
 Unable to establish a connection to the server for 164 minutes. 
 Connection will be retried in 57 seconds.
 ...
 Unable to establish a connection to the server for 164 minutes. 
 Connection will be retried in 1 seconds.
 Unable to establish a connection to the server for 164 minutes. 
 Retrying connection now.
 Unable to establish a connection to the server for 165 minutes. 
 Connection will be retried in 59 seconds.

随着秒数倒计时到 0 时将有重新检查。

任何人都可以在 AngularJS 中建议一种可以实现此倒计时的方法吗?

执行

您正在尝试的操作的一种可能方法是将$q.notify$interval结合使用。

其他消息可以通过解析和拒绝传递。当然,在任何地方完成此操作,您都可以直接打印到日志记录服务,但我认为返回这些带有 promise 行为的消息可能是合适的(如果需要,您甚至可以返回一个带有状态代码和其他数据的对象以及消息)。

在下面的示例中,我让控制器管理日志记录,并将其输出限制为 12 行。我还为连接测试指定了参数,以便它每 60 秒、20 次尝试连接一次(这些参数可以更改为以不同的间隔、不同的次数或无限期地尝试)。如果测试失败,它将每秒打印重试消息,直到重试尝试:

(function() {
  "use strict";
  var myApp = angular.module('myApp', []);
  myApp.controller('MainController', ['$scope', 'us', '$log', MainController]);
  myApp.service('us', ['$interval', '$q', '$http', '$log', usService]);
  /* Controller */
  function MainController($scope, us, $log) {
    var _data = {
        connectLog: null
      },
      _connectMessages = [],
      _MAX_LOG_LINES = 12;
    $scope.data = _data;
    _log("Starting connection test...");
    us.testConnection(60, 20) //60 seconds between tests, 20 tests (if no max specified, could run forever...)
      .then(onTestsSuccessful, onTestsFailed, onNotifying);
    function onTestsSuccessful(result) {
      _log(result);
      // do success stuff...
    }
    function onTestsFailed(result) {
      _log(result);
      // do failed stuff...
    }
    function onNotifying(result) {
      _log(result);
      //do retrying stuff...
    }
    function _log(message, deferOutput) {
      //$log.debug(message);
      _connectMessages.push(message);
      if (_MAX_LOG_LINES && _connectMessages.length > _MAX_LOG_LINES) {
        _connectMessages.splice(0, _connectMessages.length - _MAX_LOG_LINES);
      }
      if (!deferOutput) {
        _data.connectLog = _connectMessages.join('n');
      }
    }
  }
  /* Service */
  function usService($interval, $q, $http, $log) {
    var _testConnectionInterval,
      _testsRun;
    return {
      testConnection: _startTestConnection
    };
    function _startTestConnection(secondsBetweenTests, maxTests) {
      var deferred = $q.defer(),
        connectAttempts = 0;
      _cancelConnectionTest();
      _connectionTest().then(onConnectionTestSuccess, onConnectionTestFail); //immediately do first test
      _testsRun++;
      if (secondsBetweenTests > 0) {
        _testConnectionInterval = $interval(
          function repeatConnectionTest() {
            if (maxTests && _testsRun >= maxTests) {
              return _cancelConnectionTest();
            }
            deferred.notify("Retrying connection now.");
            _connectionTest().then(onConnectionTestSuccess, onConnectionTestFail);
            _testsRun++;
          },
          secondsBetweenTests * 1000); //start the countdown to the next
      }
      function onConnectionTestSuccess(result) {
        connectAttempts = 0;
        if ((maxTests && _testsRun >= maxTests) || !secondsBetweenTests) {
          deferred.resolve("Last connection test success, " + _testsRun + " tests complete.");
        } else {
          deferred.notify("Connection test success.");
        }
      }
      function onConnectionTestFail(result) {
        var minutesPassed = connectAttempts * secondsBetweenTests / 60,
            minutesRoundedToTwoDec = +(Math.round(minutesPassed + "e+2")  + "e-2");
        
        var connectMessage = "Unable to establish a connection to the server" + (connectAttempts === 0 ? "." : " for " + minutesRoundedToTwoDec + " minute" + (minutesPassed > 1 ? "s." : "."));
        connectAttempts++;
        if ((maxTests && _testsRun >= maxTests) || !secondsBetweenTests) {
          deferred.reject("Last connection test failed, " + _testsRun + " tests completed.");
        } else {
          deferred.notify(connectMessage);
          deferred.notify("Connection will be retried in " + secondsBetweenTests + " seconds.");
          var retryInterval = $interval(
            function retryMessage(counter) {
              deferred.notify(connectMessage);
              var secondsLeft = (secondsBetweenTests - 1) - counter;
              deferred.notify("Connection will be retried in " + secondsLeft + " second" + (secondsLeft > 1 ? "s." : "."));
              if (!secondsLeft) {
                $interval.cancel(retryInterval);
                retryInterval = null;
              }
            },
            1000, (secondsBetweenTests - 1));
        }
      }
      return deferred.promise;
    }
    function _connectionTest() {
      var deferred = $q.defer(),
        getBroken = {
          method: 'GET',
          url: '/api/never/gonna/give/you/up'
        };
      $http(getBroken)
        .success(function onSuccess() {
          deferred.resolve('Success!');
        })
        .error(function onError() {
          deferred.reject('Failure!');
        });
      return deferred.promise;
    }
    function _cancelConnectionTest() {
      _testsRun = 0;
      if (!_testConnectionInterval) {
        $log.debug("No previously running connection test to cancel.");
        return;
      }
      $log.debug("Cancelling connection test.");
      $interval.cancel(_testConnectionInterval);
      _testConnectionInterval = null;
    }
  }
})();
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.28/angular.min.js"></script>
<div ng-app="myApp">
  <pre ng-controller="MainController">{{data.connectLog}}</pre>
</div>

你可以像

这样从 60 秒开始倒计时:

countdown(60);
  function countdown(current_time){
    if(current_time === 0){
      //call function to connect to server
      return;
    }
    else{
      current_time--;
    }
    $scope.time = current_time;
    $timeout(function(){countdown(current_time)}, 1000);
  }

代码笔

我建议将错误消息放在 html 中并用ng-showng-if将其隐藏,然后只需更改数字,这样您就不需要一遍又一遍地附加所有文本。

我认为

没有必要使用指令,您可能可以在控制器内执行所有操作。但是,您实现了closeConnectionopenConnection,您应该编辑这些方法,添加"开始"和"停止"$interval

还要记住,$interval采用最大数量的递归,在这种情况下非常有用。

https://docs.angularjs.org/api/ng/service/$interval

function controllerFunc($scope, $interval) {
    var timer;
    var lastConnection = 0;
    $scope.message = '';
    //this is what you already have
    $interval(function () {
        us.isConnected().then(closeConnect, openConnect);
    }, 60 * 1000);
    //...
    function closeConnect() {
        //...
        $interval.cancel(timer);
        lastConnection = 0;
    }
    function openConnect() {
        //...
        timer = $interval(timerCall, 1000, 60);
        lastConnection++;
    }
    function timerCall(times) {
        $scope.message += 'Unable to establish a connection to the server for ' + lastConnection + ' minutes. ';
        var retry = 60 - times;
        $scope.message += 'Connection will be retried in '+ retry +' seconds';
    }
}

格式化邮件

在此示例中,$scope.message 是一个纯字符串,因此您不会获得任何格式,但您可以将其放入 ng-bind-html 指令中,然后将任何 html 标记添加到消息字符串中。

https://docs.angularjs.org/api/ng/directive/ngBindHtml

<div ng-bind-html="message"></div>

所以改变 js

$scope.message += '<p>Connection will be retried in '+ retry +' seconds</p>';

我确实有一个非常相似的指令,你可以在plunker上查看它

基本上它使用(1)计时器和(2)刷新状态,它也可以配置为在无秒内超时,从指令的ATTRS读取。

它确实在给定间隔后调用给定函数,它还会在$scope更改时破坏 #interval。

下面是它的代码

app.directive("csAutoRefresh", ["$interval", function ($interval) {
    var templateFn = function () {
        var template = '<div class="text-left alert alert-success nopadding"';
        template += 'style="margin-bottom: 0; margin-right: 0"> ';
        template += ' <button class="btn btn-link" data-ng-click="factory.refresh.toggle()">';
        template += '{{factory.refresh.refreshText()}}</button>';
        template += '<span>...Refreshing upload status in ';
        template += ' {{state.timer.timePending}} seconds</span>';
        template += ' </div>';
        return template;
    };
    var linkFn = function (scope) {
        scope.pauseOn = scope.pauseOn === true;
        scope.isprocessing = false;
        scope.state = {
            refresh : {
                suspend : false
            },
            timer : {
                timePending: 0,
                refreshInterval : 60
            }
        }
        function doRefresh() {
            var refresh = {
                pause: function () { scope.state.refresh.suspend = true; },
                cont: function () { scope.state.refresh.suspend = false; },
                toggle: function () { scope.state.refresh.suspend = !scope.state.refresh.suspend; },
                refreshText: function () { return scope.state.refresh.suspend ? "Resume Refresh" : "Pause Refresh"; }
            };
            return refresh;
        }
        function doTimer() {
            var timer = {
                reset: function () { scope.state.timer.timePending = scope.state.timer.refreshInterval; },
                update: function () {
                    if (scope.state.timer.timePending < 0) timer.reset();
                    scope.state.timer.timePending--;
                },
                done: function () { return scope.state.timer.timePending <= 0; },
                force: function () { scope.state.timer.timePending = 0; }
            };
            return timer;
        };
        scope.factory = {
            refresh: doRefresh(),
            timer: doTimer()
        };
        if (angular.isDefined(scope.interval) && parseInt(scope.interval) > 0) {
            scope.state.timer.refreshInterval = scope.interval;
        }
        scope.factory.timer.reset();
        scope.$watch(function () {
            return scope.state.timer.timePending;
        }, function () {
            if (!scope.factory.timer.done()) return;
            var result = scope.$eval(scope.onTimeout);
            if (angular.isObject(result) && angular.isFunction(result.then)) {
                scope.isprocessing = false;
                scope.factory.timer.reset();
                result.finally(function () { scope.factory.timer.reset(); });
            } else {
                scope.isprocessing = false;
                scope.factory.timer.reset();
            }
        });
        scope.$watch('pauseOn', function () {
            if (scope.pauseOn) {
                scope.factory.refresh.pause();
            } else {
                scope.factory.timer.reset();
                scope.factory.refresh.cont();
            }
        });
        var updateTimer = function () {
            if (scope.isprocessing) return;
            if (scope.state.refresh.suspend) return;
            scope.factory.timer.update();
        };
        var interval = $interval(updateTimer, 1000);
        scope.$on('$destroy', function () {
            $interval.cancel(interval);
        }); 
    };
    return {
        restrict: 'E',
        scope: { onTimeout: '&', pauseOn: '=', interval: '@' },
        template: templateFn,
        link: linkFn,
    };
}]);
$interval(function () {
    us.isConnected().then(closeConnect, openConnect);
}, 1000);

$interval(function () {
    us.isConnected().then(closeConnect, openConnect);
}, 20 * 1000);
后者每 20

秒(20*1000 毫秒)执行一次检查,因此除非实际检查代码有问题,否则它应该每秒运行一次。

因此,根据您希望如何向用户输出详细信息,您可能最好制作某种类型的指令并控制其中的所有内容。

执行

要执行的操作的关键是使用 $interval 服务,该服务返回一个 id:

$scope.intervalId = $interval(retryMessage, 1000);

然后,您可以根据设置的任何条件取消。

我做了一个 plnkr 来演示你想要完成的事情:

http://plnkr.co/edit/RmADu1aiOUO5o4k4pnqE?p=preview

我最近写了这个计时器,你可以从中获取一些逻辑 - Plunker。它计数并且可以在设定的时间结束

.JS

(function() {
    'use strict';
    var angularTimerApp = angular.module('angularTimerApp', []);
    angularTimerApp.directive("angularTimer", function() {
        return {
            restrict: "E",
            templateUrl: "angular-timer.html",
            scope: {endTime: "@"},
            controllerAs: "at",
            bindToController: true,
            controller: ["$scope", "$interval", function ($scope, $interval) {
                var at = this;
                at.secondsInAYear = 31536000;
                at.secondsInADay = 86400;
                at.secondsInAnHour = 3600;
                at.secondsInAMinute = 60;
                at.endTimeValue = null;
                $scope.$watch("at.endTime", function(newValue) {
                  if (angular.isDefined(newValue)) {
                    at.endTimeValue = parseInt(newValue); // No test for int
                  }
                });
                at.getTimeDisplay = function(seconds) {
                    var hoursTxt = Math.floor(((seconds % at.secondsInAYear) % at.secondsInADay) / at.secondsInAnHour);
                    var minutesTxt = Math.floor((((seconds % at.secondsInAYear) % at.secondsInADay) % at.secondsInAnHour) / at.secondsInAMinute);
                    var secondsTxt = (((seconds % at.secondsInAYear) % at.secondsInADay) % at.secondsInAnHour) % at.secondsInAMinute;
                    return ("Hours: " + hoursTxt + ", Minutes: " + minutesTxt + ", Seconds: " + secondsTxt);
                };
                at.reset = function () {
                    at.timeOffset = 0;
                    at.timeDisplay = at.getTimeDisplay(0);
                };
                at.reset();
                at.stop = function () {
                    $interval.cancel(at.timer);
                    at.timeOffset = at.time;
                };
                at.start = function() {
                    at.timeStart = (new Date()).getTime();
                    at.timer = $interval(function() {
                        at.time = Math.floor(((new Date()).getTime() - at.timeStart) / 1000) + at.timeOffset;
                        if ((at.endTimeSet) && (at.endTimeValue !== null)) {
                            if (at.time > at.endTimeValue) {
                                at.stop();
                            }
                            else {
                                at.timeDisplay = at.getTimeDisplay(at.time);
                            }
                        }
                        else {
                            at.timeDisplay = at.getTimeDisplay(at.time);
                        }
                    }, 1000);
                };
            }]
        };
    });
})();

标记

<body>
    <angular-timer end-time="10"></angular-timer>
</body>

角度计时器.html

{{at.timeDisplay}}
<br><br>
<button ng-click="at.start()">Start</button>
<br><br>
<button ng-click="at.stop()">Stop</button>
<br><br>
<button ng-click="at.reset()">Reset</button>
<br><br>
<input type="checkbox" ng-model="at.endTimeSet"> End at 27 seconds

我认为您真正想要的是一个返回承诺的服务,一个将通知发送回您的控制器的承诺。 下面是此类服务的示例:

<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js@1.4.0-rc.0" data-semver="1.4.0-rc.0" src="https://code.angularjs.org/1.4.0-rc.0/angular.js"></script>
</head>
<body ng-app="myapp" ng-controller="main">
<h1>Task Retry Example</h1>
<p>Task condition: {{value}}</p>
<button ng-click="value = true">Set task condition to true</button>
<button ng-click="reset()">Reset Task</button>
<p>{{msg}}</p>
<script>
  var app = angular.module('myapp', []);
  app.controller('main', function($scope, task){
    var myTask = function(){
      return $scope.value;
    }
    function success(result){
      $scope.msg = result;
    }
    function failure(reason){
      $scope.msg = reason;
    }
    function notify(value){
      $scope.msg = value.message;
    }
    $scope.reset = function(){
      $scope.value = false;
      $scope.msg = "Loading...";
      task.go(myTask, {maxAttempts: 3, waitTime: 3})
        .then(success, failure, notify);
    }
    $scope.reset();
  });
  app.service('task', function($q, $timeout){
    var DEFAULT_OPTIONS = {
      maxAttempts: 1,
      waitTime: 10
    };
    var thisOptions = {};
    function _countDownStep(secondsLeft, attemptsLeft, countDownProgress, taskAttemptProgress){
      if(secondsLeft <= 0){
        countDownProgress.resolve(true);
        return;
      }
      var attempt = thisOptions.maxAttempts - attemptsLeft,
            msg = "Attempt failed; retrying (" + attempt + " of " + thisOptions.maxAttempts + ") in " + secondsLeft + " seconds...";
      taskAttemptProgress.notify({
        "state": "WAITING",
        "message": msg
      })
      $timeout(function(){
        _countDownStep(secondsLeft-1, attemptsLeft, countDownProgress, taskAttemptProgress);
      }, 1000);
    }
    function _countDown(secondsLeft, attemptsLeft, progress){
      var deferred = $q.defer();
      _countDownStep(secondsLeft, attemptsLeft, deferred, progress);
      return deferred.promise;
    }
    function _attempt(task, attemptsLeft, progress){
      if(!angular.isFunction(task)) {
        progress.reject("Task is not a function.");
        return;
      }
      if(attemptsLeft <= 0){
        progress.reject("Max attempts reached.");
      }
      var result = task();
      if(result){
        progress.resolve("Successfully completed task.");
      }else {
        --attemptsLeft;
        if(attemptsLeft <= 0){
          progress.reject("Max attempts reached.");
        }
        _countDown(thisOptions.waitTime, attemptsLeft, progress).then(function(){
          var attempt = thisOptions.maxAttempts - attemptsLeft,
            msg = "Making another attempt (" + attempt + " of " + thisOptions.maxAttempts + ")...";
          progress.notify({
            "state": "TRYING",
            "message": msg
          })
          _attempt(task, attemptsLeft, progress);  
        });
      }
    }
    function _go(task, options){
      var deferred = $q.defer();
      thisOptions = options || DEFAULT_OPTIONS;
      _attempt(task, thisOptions.maxAttempts, deferred);
      return deferred.promise;
    }
    return {
      go: _go
    }
  });
</script>

最新更新