How to retain scroll position of ng-repeat in AngularJS? How to retain scroll position of ng-repeat in AngularJS? angularjs angularjs

How to retain scroll position of ng-repeat in AngularJS?


It maybe solved quite elegantly, by using scrollTop property of div. I used two directives - one handles scroll position of the wrapper element, the other register new elements.Give me a shout if anything unclear.

DEMO

JS:

.directive("keepScroll", function(){  return {    controller : function($scope){      var element = null;      this.setElement = function(el){        element = el;      }      this.addItem = function(item){        console.log("Adding item", item, item.clientHeight);        element.scrollTop = (element.scrollTop+item.clientHeight+1);       //1px for margin from your css (surely it would be possible       // to make it more generic, rather then hard-coding the value)      };    },    link : function(scope,el,attr, ctrl) {     ctrl.setElement(el[0]);    }  };}).directive("scrollItem", function(){  return{    require : "^keepScroll",    link : function(scope, el, att, scrCtrl){      scrCtrl.addItem(el[0]);    }  }})

HTML:

<div class="wrapper" keep-scroll>  <div class="item" scroll-item ng-repeat="item in items  | orderBy: '-id'">    {{ item.name }}   </div></div>


You know other people are trying to solve this problem using a different approach in terms of UI.They don't just POP new items on top, but instead they show a small clickable link on top stating how many new items are added since he last checked it.

[2 new items, Click here to refresh]item 5item 4item 3

Check out how twitter is solving this.**[Let me attach a screenshot for you shortly.]**

I know it's a bit contradicting with want you want, but perhaps this is better in terms of UX?User wants to know if there are new items coming in.


You could scroll by the amount of the height of the added elements

$scope.addNewItem = function() {    var wrapper = document.getElementsByClassName('wrapper')[0];    $scope.items = $scope.items.concat({      id: $scope.items.length,      name: "item " + $scope.items.length    });    // will fail if you observe the item 0 because we scroll before the view is updated;    wrapper.scrollTop+=101; //height+margings+paddings  };

I am using a bad practice of accessing the DOM from the controller. A more modular approach would be to create a directive which will handle all cases and change the scroll position after the view is updated.

Demo at http://jsbin.com/zofofapo/8/edit


Alternatively, for the case where the items are not equally high, you could see how much scroll is left before the insertion, and re-set it after the insertion

$scope.addNewItem = function() {    var wrapper = document.getElementsByClassName('wrapper')[0],        scrollRemaining = wrapper.scrollHeight - wrapper.scrollTop;    $scope.items = $scope.items.concat({      id: $scope.items.length,      name: "item " + $scope.items.length    });    // will fail if you observe the item 0 because we scroll before the view is updated;    $timeout(function(){      wrapper.scrollTop = wrapper.scrollHeight - scrollRemaining;    },0);  };

Demo at http://jsbin.com/zofofapo/9/edit