Penso che la soluzione iniziale è una specie di hacky ... ma ecco una modifica funzionante usandola come base.
Il problema è che la soluzione dipende dagli elementi aggiunti a ng-repeat. Se si guarda la direttiva scrollItem, fa sì che la direttiva keepScroll modifichi scrollTop solo se il linker viene eseguito. Questo succede solo quando gli oggetti vengono aggiunti, non rimossi.
La modifica ascolta invece l'evento scope.$on('$destroy')
. Il problema a quel punto è comunque che l'elemento non ha più un clientHeight perché è stato rimosso dal DOM. Quindi la soluzione è di salvare la sua altezza quando viene creata, e poi dire a keepScroll qual è l'altezza dell'elemento rimosso.
Nota: questo sembrava causare un salto di scorrimento se lo scroller era completamente in fondo, quindi dovresti esaminare il caso come eccezione.
lavoro JSBin: http://jsbin.com/geyapugezu/1/edit?html,css,js,output
Per riferimento:
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);
});
}
};
});
EDIT
Oppure, fare questo. Non c'è bisogno di scrollItem, invece guardiamo le modifiche agli elementi di ng-repeat e regoliamo di conseguenza il scrollTop.
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>
Questa è la soluzione migliore. Molto completo e segue buone pratiche angolari. Come hai detto, tuttavia, con entrambe le soluzioni, se si scorre verso il basso e si fa clic sul pulsante Rimuovi elemento, lo scorrimento salta fino a una posizione arbitraria. Quale sarebbe la causa di ciò? –