Kevin Chung
Kevin Chung

Reputation: 137

Going to the position of a filtered ng-repeat element

I have a list of ng-repeat elements that are filtered based on boolean vars in scope.

HTML:

<button ng-click="toggleSection(1)">Section 1</button>
<button ng-click="toggleSection(2)">Section 2</button>
<button ng-click="toggleSection(3)">Section 3</button>
<div id="$index" ng-repeat="section in sections track by $index | filterSections">
    ...
</div>

the filter:

myApp.filter('filterSections', function() {
    return function(input) {
        var returnArray = [];
        $.each(input, function(index, bool) {
            if (bool) returnArray.push(bool);
        });
        return returnArray;
    }
})

and in my controller:

$scope.sections = [false, false, false];
$scope.toggleSection = function(n) {
    $scope.sections[n] = $scope.sections[n] ? false : true;
}

However, I want the same toggleSection function to go to the position of the element that it shows, like...

$scope.toggleSection = function(n) {
    $scope.sections[n] = $scope.sections[n] ? false : true;
    window.scrollTo($("#" + n).position().top, 0);
}

But I can't do this because it takes time for the DOM to show the section that it's creating. Before that, the element (and its position) does not exist.

Setting a timeout would probably work, but that seems sloppy.

I suspect that I need to create some sort of callback or promise. Is that right?

Upvotes: 1

Views: 425

Answers (1)

floribon
floribon

Reputation: 19193

Custom directive, promise or anything else, if your action (scroll) relies on the DOM (and it does), you cannot avoid to execute it after an angular digest cycle.

Hence the short way to do that is indeed with a timeout:

$scope.toggleSection = function(n) {
  $scope.sections[n] = $scope.sections[n] ? false : true;
  $timeout(function() {
    window.scrollTo($("#" + n).position().top, 0);
  }, 0, false);
}

Note the third argument of $timeout being false: this is because you do not need to $apply the scope (run another digest cycle) after the timeout execute its callback since it is jQuery only.

Again, scrolling before the DOM is updated does not make sense for a browser, so you cannot avoid it (yet you can hide it).

Upvotes: 1

Related Questions