top subir bottom anchorscroll angularjs scroll angularjs-ng-repeat

subir - ¿Cómo retener la posición de desplazamiento de ng-repeat en AngularJS?



angularjs scroll to top (9)

A continuación se muestra una mejora de la versión de Arthur que impide el desplazamiento independientemente de si el elemento agregado se agrega por encima o por debajo del desplazamiento: JS Bin

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.addNewItemTop = function() { $scope.items.unshift({ id: $scope.items.length, name: "item " + $scope.items.length }); }; $scope.addNewItemMiddle = function() { $scope.items.splice(5, 0, { id: $scope.items.length, name: "item " + $scope.items.length }); }; $scope.addNewItemBottom = 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.group("Item"); console.log("OffsetTop: " + item.offsetTop); console.log("ScrollTop: " + element.scrollTop); if(item.offsetTop <= element.scrollTop) { console.log("Adjusting scorlltop position"); element.scrollTop = (element.scrollTop+item.clientHeight+1); //1px for margin } else { console.log("Not adjusting scroll"); } console.groupEnd("Item"); }; }, 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]); } }; });

.wrapper { width: 200px; height: 300px; border: 1px solid black; overflow: auto; /* Required for correct offsetParent */ position: relative; } .item { background-color: #ccc; height: 100px; margin-bottom: 1px; }

<!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"> {{ item.name }} </div> </div> <button ng-click="addNewItemTop()"> Add New Item Top </button> <button ng-click="addNewItemMiddle()"> Add New Item Middle </button> <button ng-click="addNewItemBottom()"> Add New Item Bottom </button> </body> </html>

DEMO

La lista de objetos se renderiza usando ng-repeat . Supongamos que esta lista es muy larga y el usuario se desplaza hacia abajo para ver algunos objetos.

Mientras el usuario está observando un objeto, se agrega un nuevo elemento al principio de la lista. Esto hace que el objeto observado cambie su posición, es decir, el usuario ve de repente algo más en el mismo lugar del objeto observado.

¿Cómo haría para mantener el objeto observado en el mismo lugar cuando se agregan nuevos elementos?

DEMO


Debe agregar una directiva scrollspy a su contenedor que actualice su posición en cada desplazamiento de usuario, y reciba notificaciones en cada repetición para que pueda reposicionarse a su estado guardado. tu html podría verse así

<div scrollspy id="myscrollspy"> <ul> <li ng-repeat="" notifyscroll></li> </ul> </div>

el espía de desplazamiento tendría los ajustes de desbordamiento de css requeridos y scroll-x o scroll-y para realizar un seguimiento del desplazamiento actual y evitar contaminar el alcance; también debería observar un evento que provenga de la repetición ng que le indique que ocurrió un cambio y Debería configurar el scrool.

ng-repeat podría notificarlo adjuntando un nuevo desplazamiento de notificación de directiva que inicia un evento. No estoy seguro de si la versión curretn de angular admite el evento postrender.

La forma de colocar el desplazamiento dependerá de si está utilizando una biblioteca de terceros $ .scrollTop (pos) o no. Esto lo hará o debería. Espero eso ayude


Hay, creo, solo algunas posibles soluciones

1) No agregue el elemento (según la otra respuesta)

2) Agregue el artículo en la parte inferior, para que la lista no se mueva.

3) Agregue el elemento en la parte superior y desplace la pantalla automáticamente para que se tenga en cuenta la altura del nuevo elemento, y todo se mantenga aparentemente como antes. La lista se moverá hacia abajo, pero la pantalla visible también se moverá, por lo que se verá relativamente nada que se mueva. Bueno, otros elementos que no forman parte de la lista lo harán, pero que en realidad podrían verse bastante bien ...


Podría aplazar la adición de los elementos hasta que el usuario desplace la parte superior de la lista a la vista. No tiene sentido renderizar los elementos antes.

Podría verse así (quizás con una animación adicional).


Puede desplazarse por la cantidad de la altura de los elementos agregados

$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 };

Estoy usando una mala práctica de acceder al DOM desde el controlador. Un enfoque más modular sería crear una directiva que manejará todos los casos y cambiará la posición de desplazamiento después de que se actualice la vista.

Demostración en http://jsbin.com/zofofapo/8/edit

Alternativamente, para el caso en que los artículos no sean igualmente altos, podría ver cuánto desplazamiento queda antes de la inserción y volver a establecerlo después de la inserción.

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

Demostración en http://jsbin.com/zofofapo/9/edit


Puede resolverse con bastante elegancia, utilizando la propiedad scrollTop de div . Utilicé dos directivas: una maneja la posición de desplazamiento del elemento envoltorio y la otra registra elementos nuevos. Dame un grito si algo no está claro.

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>


Puedes resolver este problema con ng-animate:

.animation(''.keep-scroll'', [function () { var keepScroll = function(element, leave){ var elementPos = element.offset().top; var scrollPos = document.body.scrollTop; if(elementPos < scrollPos){ var height = element[0].clientHeight; if(leave){ height *= (-1); } document.body.scrollTop += height; } }; return { enter: function (element, doneFn) { keepScroll(element); doneFn(); }, leave: function (element, doneFn) { keepScroll(element, true); doneFn(); } }; }])

Simplemente asigne la clase css .keep-scroll a sus elementos repetidos, como esto:

<div ng-repeat="item in items" class="keep-scroll">...</div>


Usted sabe que otras personas están tratando de resolver este problema utilizando un enfoque diferente en términos de IU. No solo publican los elementos nuevos en la parte superior, sino que muestran un pequeño enlace que se puede hacer clic en la parte superior que indica cuántos elementos nuevos se agregaron desde que lo revisó por última vez.

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

Echa un vistazo a cómo Twitter está resolviendo esto.

Sé que es un poco contradictorio con querer lo que quieres, pero tal vez esto es mejor en términos de UX? El usuario quiere saber si hay nuevos artículos entrando.


La forma de FaceBook: Todos están sugiriendo esto, aquí hay una pseudo implementación:

MYEXAMPLE

A medida que se agregan nuevos objetos, póngalos en "More Queue".

<div style="height:15px"> <button ng-if="moreQueue"ng-click="transferWait()"> See More {{ moreQueue }} </button > </div> <div class="wrapper"> <div class="item" ng-repeat="item in items | orderBy: ''-id''"> {{ item.name }} </div> </div>

MessageHandlerController tendrá al menos 2 arreglos (debemos tratar como el b / c de la cola que vamos a abrir de abajo hacia arriba.

  • Mensajes activos
  • Esperando mensajes

A medida que su Signal R / Service Bus llena su WaitingQueue , su ng-if aumenta el tamaño y su $scope.SizeOfWaitingQueue=SizeOfWaitingQueue() . Este proceso de reasignación debería ocurrir cada iteración para que no tengas que revisar sucia.