Мы видели как создать счетчик на AngularJS, который мы увеличивали или уменьшали с помощью нажатий на кнопку. В этом примере мы будем автоматически увеличивать счетчик с течением времени.

Не забудьте ознакомиться с другими примерами счетчиков!

Планирование будущего выполнения, используя $timeout

examples/angular/automatic_counter.html

<script src="angular.min.js"></script>
<script>
angular.module('CounterApp', [])
    .controller('CounterController', function($scope, $timeout) {
        $scope.counter = 0;
        var updateCounter = function() {
            $scope.counter++;
            $timeout(updateCounter, 1000);
        };
        updateCounter();
    });
</script>
<div ng-app="CounterApp">
   <div ng-controller="CounterController">
   {{counter}}
   </div>
</div>
Try!

В этом примере функция контроллера ожидает два параметра. $scope - содержит атрибуты, с которыми мы взаимодействуем в нашем HTML, и $timeout - это функция, похожая на setTimeout из обычного Javascript. (На самом деле это называется внедрение зависимости, а не параметры, но пока не будем беспокоиться об этом. Особенно, пока я сам этого не понимаю.)

$timeout это функция, которая принимает два параметра: функцию обратного вызова (коллбек) и время, определенное в милисекундах. Она откладывает выполнение полученной функции до истечения заданного времени. Если передать 1000 в качестве второго параметра, мы отложим выполнение переданной функции на 1 секунду после того, как собственно вызовем саму эту функцию.

Внутри функции контроллера первое что мы делаем, это создаем атрибут counter и устанавливаем ему значение по умолчанию 0. Мы хотим, чтобы отчет начинался с 0.

Затем мы создаем функцию updateCounter, которая при вызове будет увеличивать counter и использовать $timeout, чтобы вызвать себя же спустя 1 секунду. Это значит, что каждый раз при вызове функции updateCounter она увеличит счетчик и запланирует в системе повторный вызов через 1 секунду. Это значит, функция будет запускаться каждую секунду.

Затем последний шаг это первый вызов updateCounter для запуска бесконечного цикла.

Счетчик с кнопкой Стоп

Этот счетчик будет увеличиваться каждую секунду, но что если мы захотим остановить его? Мы увидим пример с дополнительной кнопкой, которая остановит счетчик.

$timeout возвращает объект promise, который мы можем использовать позднее, чтобы отключить таймер. Чтобы сделать promise доступным, когда нам будет нужно, я создал переменную timer и присвоил ей то, что вернула функция $timeout. (Конечно, я мог бы использовать здесь любое другое имя.)

Затем я добавил кнопку в HTML и указал в атрибуте ng-click вызов метода stopCounter.

Осталось сделать только метод stopCounter.

Для начала я создал его, используя var stopCounter = function() { ... } как и updateCounter, но это не сработало. Так как мы хотим вызывать метод из HTML, нам нужно добавить этот метод в $scope. Следовательно, я должен был заменить определение на: $scope.stopCounter = function() { ... }.

Внутри у нас есть выражение $timeout.cancel(timer);, которое отменит работу таймера. Попробуйте!

examples/angular/automatic_counter_with_stop.html

<script src="angular.min.js"></script>
<script>
angular.module('CounterApp', [])
    .controller('CounterController', function($scope, $timeout) {
        var timer;
        $scope.counter = 0;
        $scope.stopCounter = function() {
            $timeout.cancel(timer);
        };
        var updateCounter = function() {
            $scope.counter++;
            timer = $timeout(updateCounter, 1000);
        };
        updateCounter();
    });
</script>
<div ng-app="CounterApp">
   <div ng-controller="CounterController">
   {{counter}}
   <button ng-click="stopCounter()">Stop</button>
   </div>
</div>
Try!

Счетчик с кнопками запуска и остановки

Еще одна штука, которую я хочу показать, это как я добавил еще одну кнопку для запуска счетчика дальше.

Первая версия была простой. Я просто добавил кнопку и новую функцию:

$scope.startCounter = function() {
    updateCounter();
};

Проблема такого решения в том, что оно позволяло мне нажать несколько раз на кнопку старта, и счетчик начинал увеличиваться слишком быстро, перескакивая иногда на 2 или 3 шага. В реальности происходило вот что: каждый раз, когда я нажимал на кнопку start, запускался новый таймер и несколько таймеров работало параллельно.

Я должен был как-то убедиться, что в одно время работает только один таймер. Либо заблокировать кнопку start после нажатия, либо проверять, что таймер уже запущен, и запускать новый только если это не так.

Я выбрал это решение, так как был больше заинтересован в решении на JavaScript/AngularJS. Я сделал два изменения. В функции stopCounter я добавил

timer = null;

В конце концов все что у нас есть, это отмененный таймер, и нет смысла держать неиспользуемые объекты.

Затем в методе startCounter я смог проверить, является ли timer null или нет, и создать новый объект $timeout, если таймер отсутствует.

examples/angular/automatic_counter_with_stop_start.html

<script src="angular.min.js"></script>
<script>
angular.module('CounterApp', [])
    .controller('CounterController', function($scope, $timeout) {
        var timer;
        $scope.counter = 0;
        $scope.stopCounter = function() {
            $timeout.cancel(timer);
            timer = null;
        };
        $scope.startCounter = function() {
            if (timer === null) {
                updateCounter();
            }
        };
        var updateCounter = function() {
            $scope.counter++;
            timer = $timeout(updateCounter, 1000);
        };
        updateCounter();
    });
</script>
<div ng-app="CounterApp">
   <div ng-controller="CounterController">
   {{counter}}
   <button ng-click="stopCounter()">Stop</button>
   <button ng-click="startCounter()">Start</button>
   </div>
</div>
Try!