Подтвердить что ты не робот

Как я могу показать сообщение, отсчитывающее каждую секунду с помощью 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, чтобы я мог достичь этого обратного отсчета?

4b9b3361

Ответ 1

Я не думаю, что использование директивы необходимо, возможно, вы можете делать все внутри контроллера. Однако вы внедрили closeConnection и openConnection, вы должны отредактировать эти методы, добавив "start" и "stop" $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>';

Ответ 2

Один из возможных способов сделать то, что вы пытаетесь, - использовать $q.notify в сочетании с $interval.

Другие сообщения могут передаваться с разрешением и отклонением. Конечно, в любом месте это делается, вы можете просто напечатать прямо в службе ведения журнала, но я подумал, что было бы целесообразно вернуть эти сообщения с помощью поведения обещаний (где, если бы вы хотели, вы могли бы вместо этого вернуть объект, полный код состояния и другие данные вместе с сообщением).

В приведенном ниже примере я позволил контроллеру управлять журналом и ограничить его вывод до 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>

Ответ 3

Вы можете сделать обратный отсчет с 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-show или ng-if, а затем просто изменить номер, чтобы вам не нужно было добавлять весь этот текст снова и снова.

Ответ 4

У меня есть директива для очень схожих целей, вы можете проверить ее здесь, на plunker

в основном он использует (1) таймер и (2) состояние обновления, он также конфигурируется, как и в секундах для тайм-аута, считанных с attrs директивы.

Он вызывает заданную функцию после заданного интервала, а также уничтожает #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,
    };
}]);

Ответ 5

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

Не

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

Последний выполняет проверку каждые 20 секунд (20 * 1000 мс), поэтому, если фактический код проверки не работает, он должен запускаться каждую секунду.

Ответ 6

Таким образом, в зависимости от того, как вы хотите вывести данные пользователю, вы, вероятно, лучше всего придумаете эту директиву и будете контролировать все в ней.

Ключом к тому, что вы хотите сделать, является использование службы $interval, которая возвращает id:

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

Затем вы можете отменить в зависимости от условий, которые вы установили.

Я сделал plnkr, демонстрируя то, что вы хотите выполнить:

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

Ответ 7

Недавно я написал этот таймер, из которого вы можете взять некоторую логику - 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>

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

Ответ 8

Я думаю, что вы действительно хотите, это сервис, который возвращает обещание, обещание, которое отправляет уведомления обратно вашему контроллеру. Вот пример такого сервиса:

<!DOCTYPE html>
<html>
<head>
<script data-require="[email protected]" 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>