Mike Chamberlain
Mike Chamberlain

Reputation: 42480

Avoiding $timeout() to force digests to complete in Angular

In my Angular app the following sequence of events needs to happen:

  1. User clicks a button
  2. A previously hidden div is shown by flipping a boolean scope variable to which an ng-show is bound
  3. The newly shown view, which is further down the page, should be scrolled into view.

Currently, the scrolling does not work because this is all happening within the same digest. This means that the new state of the ng-show bound variable has not had a chance to update the DOM. Therefore, we attempt to scroll to an element that, as far as the DOM is concerned, is not yet visible.

This is mitigated by wrapping the scroll call with a $timeout, which forces all digest(s) to complete before attempting to scroll. While this works, it feels like a hack, and I want to know if there is a better way of doing this.

Here is a fiddle that demonstrates the problem:

http://jsfiddle.net/fNhnj/3/

(Note that this code is a simplified version of my real code just to demonstrate the problem, and I realize it does not follow best practices. Rest assured that my real code does not perform direct DOM manipulation in the controller.)

View:

<div ng-app="app" ng-controller="MyCtrl" id="container">
    <button ng-click="scroll()">Unhide element and scroll</button> (will not work until $timeout call is uncommented)
    <div style="height:1000px; background-color: #ddddd0">
    </div>
    <div id="target" ng-show="isVisible">section to scroll to</div>
</div>

JS:

angular.module("app", [])

.controller('MyCtrl', 
    function MyCtrl($scope, $timeout) {
        $scope.isVisible = false;
        $scope.scroll = function() {
            $scope.isVisible = true;
            // uncommenting the $timeout fixes it, but feels like a hack
            //$timeout(function() {
                $('body').animate({
                    scrollTop: $("#target").offset().top
                }, 500);
            //});
        };
    }
);

Upvotes: 2

Views: 1235

Answers (1)

Abhishek Jain
Abhishek Jain

Reputation: 2977

Using $timeout is not such a big issue, because the code you are running is not an angular code, and you need to communicate to angular in some way that it needs to refresh the view.

Upvotes: 6

Related Questions