JavaScript 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);
            //});
        };
    }
);
Answer:1

Often I have maps with a whole lot of markers (let's say representing shops). I have infowindows that display basic info on the shop represented by the marker, then, on the infoWindow HTML I like to ...

Often I have maps with a whole lot of markers (let's say representing shops). I have infowindows that display basic info on the shop represented by the marker, then, on the infoWindow HTML I like to ...

I want to create a html5 audioplayer for my band's website. The audiofiles are stored in the database as mp3, ogg and wav files to support all major browsers on their current versions. First, all ...

I want to create a html5 audioplayer for my band's website. The audiofiles are stored in the database as mp3, ogg and wav files to support all major browsers on their current versions. First, all ...

I have two static pages from a user which is deployed on their servers. Currently I am making a call to their server from code behind and loading them. But now I don't want to make a code behind call. ...

I have two static pages from a user which is deployed on their servers. Currently I am making a call to their server from code behind and loading them. But now I don't want to make a code behind call. ...

  1. load website inside iframe
  2. load webpage into iframe
  3. html load webpage in iframe
  4. load external webpage in iframe

I read this blog post on how to potentially pass a primitive by reference to a function in JavaScript. At first glance it seems like it could work but I'm wondering what potential pitfalls could come ...

I read this blog post on how to potentially pass a primitive by reference to a function in JavaScript. At first glance it seems like it could work but I'm wondering what potential pitfalls could come ...