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

时间:2015-04-17 02:34:38

标签: 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中建议我可以实现这个倒计时吗?

8 个答案:

答案 0 :(得分:9)

执行您尝试的操作的一种可能方法是将$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>

答案 1 :(得分:6)

您可以在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);
  }

codepen

我建议将错误消息放在html中,然后用ng-showng-if隐藏它,然后只需更改号码,这样就不需要附加所有内容那个文字一遍又一遍。

答案 2 :(得分:4)

我不认为使用指令是必要的,你可以在控制器内做所有事情。无论您实施了closeConnection还是openConnection,都应该修改这些方法,然后添加&#39; start&#39;并且&#39;停止&#39; $interval

还要记住$interval需要最多的递归次数,在这种情况下非常有用。

https://docs.angularjs.org/api/ng/service/ $间隔

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>';

答案 3 :(得分:2)

我的指令非常相似,您可以查看here on plunker

基本上它使用(1)计时器&amp; (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,
    };
}]);

答案 4 :(得分:1)

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

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

后者每隔20秒(20 * 1000ms)进行一次检查,所以除非实际的检查代码有问题,否则它应该每秒运行一次。

答案 5 :(得分:1)

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

执行您想要做的事情的关键是使用$ interval服务,该服务返回一个id:

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

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

我做了一个plnkr来展示你想要实现的目标:

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

答案 6 :(得分:1)

我最近写了这个计时器,你可以从中得到一些逻辑 - 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>

角timer.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

答案 7 :(得分:1)

我认为你真正想要的是一个返回承诺的服务,一个将通知发送回控制器的承诺。 Here's这种服务的一个例子:

<!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>