0

I have a small AngularJS app which searches and retrieves a listing of users and their next scheduled meeting (assuming one is scheduled in the next 8 hours) brought back from the server using JSON with the time format in UTC for ease of calculation to local times. Each user could have a different status (busy until x time, free until x time).

What I would like to accomplish is to be able to update the DOM with time remaining until the meeting scheduled has completed or time left until the meeting starts. I have some code working sort of, but because I am apparently not doing this correctly with only a few entries in the result set it brings the browser to a near standstill. Any suggestions would be appreciated!

My current code consists of the following snippets:

[Main Page]

<tr ng-repeat="item in pagedItems[currentPage-1] | orderBy:sortingOrder:reverse" ng-class="{success:item._freeBusy=='Free', error:item._freeBusy=='Busy'}">
       <td>{{item._firstName}}</td>
       <td>{{item._lastName}}</td>
       <td>{{item._facilityName}}</td>
       <td>{{item._extension}}</td>
       <td>{{item._description}}</td>
       <td><a ng-hide="!item._extension" ng-click="dial(item)">Dial</a></td>
       <td><button class="btn btn-primary" ng-click="openDetails(item)">Details</button></td>
       <td>{{item._freeBusy}} {{item._timeLeft}} {{calculateTime(item._freeBusyTime,$index)}}</td>
 </tr>

[Controller]

        $scope.timeUntil = function(s) {
        function isoToObj(s) {
            var b = s.split(/[-T+:]/i);
            return new Date(Date.UTC(b[0], --b[1], b[2], b[3], b[4], b[5]));
        }
        // Utility to add leading zero
        function z(n) {
            return (n < 10 ? '0' : '') + n;
        }
        // Convert string to date object
        var d = isoToObj(s);
        var diff = d - new Date();
        // Allow for previous times
        var sign = diff < 0 ? '-' : '';
        diff = Math.abs(diff);
        // Get time components
        var hours = diff / 3.6e6 | 0;
        var mins = diff % 3.6e6 / 6e4 | 0;
        var secs = Math.round(diff % 6e4 / 1e3);
        // Return formatted string
        return sign + z(hours) + ' Hours ' + z(mins) + ' Min' + ':' + z(secs);// + ':' + z(secs)
    }

    $scope.calculateTime = function(s, idx) {
        timeoutID = $timeout(function() {
            $scope.items[idx]._timeLeft = $scope.timeUntil(s);
            $scope.calculateTime(s, idx);
        }, 1000);
    };

EDIT

I understand the issues as mentioned below, what I am struggling with is how to register this corretly. As it could be up to 15+ separate times updating to a single tick that's where I am getting lost.

0

3 Answers 3

1

You are registering way more timeouts than you think. Every time angular renders your view, you register new timeout handlers. Add a counter of your callback and watch the count go:

$scope.called = 0;

$scope.calculateTime = function(s, idx) {
    timeoutID = $timeout(function() {
        $scope.items[idx]._timeLeft = $scope.timeUntil(s);
        $scope.calculateTime(s, idx);
        console.log(++$scope.called);
    }, 1000);
};

See this plunk where I reproduced the bug: http://plnkr.co/edit/qJ4zDl6gc5C7Edg0T0gB. Just run it and watch the counter.

Why do you want to update your _timeLeft in the rendering cycle? Why not do this:

$scope.called = 0;

setInterval(function() {
  angular.forEach($scope.items, function function_name(item) {
    item._timeLeft = $scope.timeUntil(item._freeBusyTime);
  });
  console.log(++$scope.called);
  $scope.$apply();
}, 1000);

See new plunk: http://plnkr.co/edit/rVJ6p4VXDQvt7rjT6eka

Sign up to request clarification or add additional context in comments.

1 Comment

This worked better for me as it was part of a filtered list it is now associated with the specific element versus keeping track of another array which on page change will not update to the correct record without extra functions.
1

You are calling $scope.calculateTime recursively! And you are modifying the list of items during the ng-repeat, which also causes and endless loop.

How about this: http://plnkr.co/edit/0JqK96irV4ETdWZYxO3P?p=preview

changed the html to refer to a separate array that doesn't affect ng-repeat:

<td>in {{_timeLeft[$index]}}</td>

which is updated as follows:

$scope._timeLeft = [];

var intervalID = window.setInterval(function() {
    for (var i=0; i<$scope.items.length; i++) {
      $scope._timeLeft[i] = $scope.timeUntil($scope.items[i]._freeBusyTime);
    }
    $scope.$apply();
}, 1000);

Note that $scope.$apply() is required to let Angular know that '_timeLeft' has been modified, which updates all references to it.

Comments

0

I do not think you should call $timeout every view update. Instead, you can count down time within your controller and let view show the timer.

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.