2015-11-15 6 views
7

ho cercato di lavorare dalla soluzione a questocome mantenere la posizione di scorrimento di ng-repeat in AngularJS quando si rimuove un elemento dalla cima

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

per ottenere mantenendo la posizione di scorrimento quando si rimuove la parte superiore elemento in una ng-repeat ma non riuscivo a capire il codice per farlo.

Inoltre, nota a margine, l'elenco deve essere stampato nello stesso ordine dell'array di elementi, non nel retro come nell'esempio.

codice della soluzione:

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]); 
    } 
    } 
}) 

Quello che ho provato a fare è stato cambiare

element.scrollTop = (element.scrollTop + item.clientHeight+1) 

a

element.scrollTop = (element.scrollTop - item.clientHeight+1) 

e la stampa in ordine di 'id' non '-id'

risposta

5

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> 
+0

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ò? –

0

Spero $anchorScroll può aiutarti. Segui lo link.

+0

Questo in realtà non fare quello che voglio. Non tutti gli elementi avranno la stessa altezza e non vorrei necessariamente scorrere fino alla cima dell'articolo. –

+1

Ma grazie, @ lokeshjain2008 –

0

Non sono sicuro di aver capito correttamente, ma è possibile ottenere ciò che si desidera ascoltando l'array di articoli e l'elemento da rimuovere.

Spero che questo vi aiuterà

http://plnkr.co/edit/buGcRlVGClj6toCVXFKu?p=info

Ecco quello che ho fatto:

Aggiunto una proprietà di altezza per gli elementi

for (var i = 0; i < 20; i++) { 
    $scope.items[i] = { 
     id: i, 
     name: 'item ' + i, 
     height: (Math.random()*100)+30 
    }; 
} 

style: height proprietà all'interno del file html

<div class="wrapper" keep-scroll> 
    <div class="item" scroll-item ng-repeat="item in items" style="height:{{item.height}}px"> 
     {{ item.name }} 
    </div> 
</div> 

deleteItem metodo all'interno del DemoCtrl

$scope.deleteItem = function() { 
    var itemToDelete = $scope.items[0]; 
    $scope.items.splice(0,1); 
    $scope.$broadcast("scrollFix",itemToDelete); 
}; 

di quanto io ascolto scrollFix evento all'interno della direttiva keepScroll

$scope.$on('scrollFix',function(event,data){ 
    element.scrollTop = element.scrollTop - data.height; 
}); 
+0

Perché la proprietà height è necessaria per aggiungere un elemento sebbene non fosse necessario per rimuoverlo come nella soluzione precedente collegata? –

+0

Anche la trasmissione di un evento nello scope di root non è una buona pratica. Mi piacerebbe utilizzare una direttiva come nella soluzione originale e secondo le migliori pratiche per la manipolazione degli elementi DOM. –

+0

Sì, hai ragione, muhamed. Non è necessario definire l'altezza per gli oggetti. La soluzione di Foglerek è più conveniente – huseyinozcan