9

I am working with an AngularJS powered page, and I need to display a running clock inside a read-only input text field (two way bound with data-ng-model). To simulate a running clock, I am using a JavaScript scheduler with setTimeout to call a function every 1000 milliseconds, which updates the $scope'd property value which in turn is bound to that input text field. Somehow the value in the input field is not getting updated. So I placed a <pre /> tag and updated its content using a jQuery selector. That is working fine, so I need help getting the input text field value to also get updated every second.

I have set up a jsFiddle for this example.

The HTML is below:

<body data-ng-app="formApp">
    <div data-ng-controller="FormCtrl">
        Current Date and Time <input type="text" data-ng-model="formData.currentDateTime" readonly="readonly" size="60" />
    </div>
    <pre id="currentDateTime" style="font-size:1.5em;">
    </pre>
</body>

The AngularJS app module and controller are declared as follows:

(function() {
    var formApp = angular.module("formApp", []);

    formApp.controller("FormCtrl", function ($scope) {
        $scope.formData = {};
        $scope.formData.currentDateTime = new Date().toString();

        (function updateCDT() {
            $scope.formData.currentDateTime = new Date().toString();
            document.getElementById("currentDateTime").innerHTML = $scope.formData.currentDateTime;
            setTimeout(updateCDT, 1000);
        })();
    });
})();
Web User
  • 7,438
  • 14
  • 64
  • 92

4 Answers4

11

you need to use $scope.$apply() or angulars $timeout to reflect changes since setTimeout is outside the scope of angularjs

using $scope.$apply()

apply $scope.$apply() inside anonymous function of setTimeout(function(){},1000) and then call the actual function like below

   (function updateCDT() {
        $scope.formData.currentDateTime = new Date().toString();
        document.getElementById("currentDateTime").innerHTML
  = $scope.formData.currentDateTime;
        setTimeout(function(){
          $scope.$apply();
            updateCDT()
        }, 1000);

fiddle for $scope.$apply()

using $timeout (dont forget to inject it into controller)

 (function updateCDT() {
            $scope.formData.currentDateTime = new Date().toString();
            document.getElementById("currentDateTime").innerHTML
    = $scope.formData.currentDateTime;
            $timeout(updateCDT, 1000);
          
        })();

fiddle for $timeout

Community
  • 1
  • 1
A.B
  • 20,110
  • 3
  • 37
  • 71
  • I went with injecting `$timeout` into the controller and using it because it looks cleaner **and** because Chrome Developer Tools shows up an error `Error: [$rootScope:inprog] $apply already in progress` when using `$scope.$apply();`. Thanks for the immediate help! – Web User Apr 18 '15 at 15:39
3

Instead of using setTimeout() you can use $timeout() which will call $apply() internally thereby telling angular to run a digest.

Just remember to inject $timeout as a dependency

DEMO

charlietfl
  • 170,828
  • 13
  • 121
  • 150
2

If you modify the scope outside of angular like with setTimeout you need to call $scope.$apply.

jcubic
  • 61,973
  • 54
  • 229
  • 402
  • Chrome Developer Tools shows up an error `Error: [$rootScope:inprog] $apply already in progress` when using `$scope.$apply();`, so I went with `$timeout`. – Web User Apr 18 '15 at 15:40
  • 1
    @WebUser You can use `if(!$scope.$$phase) { //call $apply }` – jcubic Apr 18 '15 at 15:43
  • 1
    @WebUser or you can apply $scope.$apply() inside anonymous function of setTimeout(function(){ $scope.$apply(); updateCDT()},1000) and then call the actual function – A.B Apr 18 '15 at 15:50
2

Just do the same as in the pre tag:

document.getElementById("input-field").value=$scope.formData.currentDateTime;

Hope it helps!

JJPunch
  • 176
  • 1
  • 10
  • I guess I could have done that, but I wanted to do it the AngularJS way :-) Thanks for suggesting it though. – Web User Apr 18 '15 at 15:41