I tried to work from the solution to this
How to retain scroll position of ng-repeat in AngularJS?
to achieve retaining the scroll position when removing the top item in an ng-repeat but couldn't figure out the code to do so.
Also, side note, the list should print in the same order as the items array, not in the reverse as the example does.
The solution's code:
angular.module("Demo", [])
.controller("DemoCtrl", function($scope) {
$scope.items = [];
for (var i = 0; i < 10; i++) {
$scope.items[i] = {
id: i,
name: 'item ' + i
};
}
$scope.addNewItem = function() {
$scope.items = $scope.items.concat({
id: $scope.items.length,
name: "item " + $scope.items.length
});
};
})
.directive("keepScroll", function(){
return {
controller : function($scope){
var element = 0;
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
};
},
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]);
}
}
})
What I tried doing was changing
element.scrollTop = (element.scrollTop + item.clientHeight+1)
to
element.scrollTop = (element.scrollTop - item.clientHeight+1)
and printing in order by 'id' not '-id'
I think the initial solution is kind of hacky... but here's a working edit using it as the basis.
The problem is that the solution depends on items being added to the ng-repeat. If you look at the scrollItem directive, it only causes the keepScroll directive to readjust scrollTop if the linker gets executed. This only happens when items get added, not removed.
The edit instead listens to the scope.$on('$destroy')
event. The issue at that point is however, that the element no longer has a clientHeight because it has been removed from the DOM. So the solution is to save its height when it gets created, and then instead tell keepScroll what the height of the removed element was.
Note: This seemed to cause a scroll jump if the scroller was all the way to the bottom, so you'd need to look into that case as an exception.
Working JSBin: http://jsbin.com/geyapugezu/1/edit?html,css,js,output
For reference:
HTML
<!DOCTYPE html>
<html>
<head>
<script src="//code.angularjs.org/1.3.0-beta.7/angular.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body ng-app="Demo" ng-controller="DemoCtrl">
<div class="wrapper" keep-scroll>
<div class="item" scroll-item ng-repeat="item in items | orderBy: 'id'">
{{ item.name }}
</div>
</div>
<button ng-click="removeItem()">
Remove item
</button>
</body>
</html>
CSS
.wrapper {
width: 200px;
height: 300px;
border: 1px solid black;
overflow: auto;
}
.item {
background-color: #ccc;
height: 100px;
margin-bottom: 1px;
}
JS
angular.module("Demo", [])
.controller("DemoCtrl", function($scope) {
$scope.items = [];
for (var i = 0; i < 10; i++) {
$scope.items[i] = {
id: i,
name: 'item ' + i
};
}
$scope.removeItem = function() {
$scope.items = $scope.items.slice(1);
};
})
.directive("keepScroll", function(){
return {
controller : function($scope){
var element = 0;
this.setElement = function(el){
element = el;
};
this.itemRemoved = function(height){
element.scrollTop = (element.scrollTop - height - 1); //1px for margin
console.log("Item removed", element.scrollTop);
};
},
link : function(scope,el,attr, ctrl) {
ctrl.setElement(el[0]);
}
};
})
.directive("scrollItem", function(){
return {
require : "^keepScroll",
link : function(scope, el, att, scrCtrl){
var height = el[0].clientHeight;
scope.$on('$destroy', function() {
scrCtrl.itemRemoved(height);
});
}
};
});
Or, do this. No need for scrollItem, instead we watch changes to the ng-repeat items and readjust the scrollTop accordingly.
JSBin: http://jsbin.com/dibeqivubi/edit?html,css,js,output
JS
angular.module("Demo", [])
.controller("DemoCtrl", ['$scope', function($scope) {
$scope.items = [];
for (var i = 0; i < 10; i++) {
$scope.items[i] = {
id: i,
name: 'item ' + i
};
}
$scope.removeItem = function() {
$scope.items = $scope.items.slice(1);
};
}])
.directive("keepScroll", function() {
return {
link : function(scope,el,attr, ctrl) {
var scrollHeight;
scope.$watchCollection('items', function(n,o) {
// Instantiate scrollHeight when the list is
// done loading.
scrollHeight = scrollHeight || el[0].scrollHeight;
// Adjust scrollTop if scrollHeight has changed (items
// have been removed)
el[0].scrollTop = el[0].scrollTop - (scrollHeight - el[0].scrollHeight);
// Remember current scrollHeight for next change.
scrollHeight = el[0].scrollHeight;
});
}
};
});
HTML
<!DOCTYPE html>
<html>
<head>
<script src="//code.angularjs.org/1.3.0-beta.7/angular.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body ng-app="Demo" ng-controller="DemoCtrl">
<div class="wrapper" keep-scroll>
<div class="item" ng-repeat="item in items | orderBy: 'id'">
{{ item.name }}
</div>
</div>
<button ng-click="removeItem()">
Remove item
</button>
</body>
</html>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With