versiones otro llamar funciones funcion ejemplos controlador javascript angularjs angularjs-ng-repeat

javascript - otro - ng-repeat angularjs



¿Cómo mejorar el rendimiento de ngRepeat en un gran conjunto de datos(angular.js)? (12)

A veces, lo que sucedió es que obtienes los datos del servidor (o back-end) en pocos ms (por ejemplo, supongo que son 100 ms), pero lleva más tiempo mostrarlos en nuestra página web (digamos que tarda 900 ms en monitor).

Entonces, lo que está sucediendo aquí es 800ms Toma solo para renderizar página web.

Lo que he hecho en mi aplicación web es que he usado la paginación (o puedes usar el desplazamiento infinito también) para mostrar la lista de datos. Digamos que estoy mostrando 50 datos / página.

Así que no cargaré todos los datos a la vez, solo 50 datos que estoy cargando inicialmente, lo cual toma solo 50ms (estoy asumiendo aquí).

así que el tiempo total aquí disminuyó de 900ms a 150ms, una vez que el usuario solicite la próxima página, luego muestra los siguientes 50 datos, y así sucesivamente.

Espero que esto te ayude a mejorar el rendimiento. Todo lo mejor

Tengo un enorme conjunto de datos de varios miles de filas con alrededor de 10 campos cada uno, alrededor de 2MB de datos. Necesito mostrarlo en el navegador. El enfoque más directo (buscar datos, ponerlo en $scope , let ng-repeat="" hacer su trabajo) funciona bien, pero congela el navegador durante aproximadamente medio minuto cuando comienza a insertar nodos en DOM. ¿Cómo debería abordar este problema?

Una opción es agregar filas a $scope incremental y esperar a que ngRepeat termine de insertar un fragmento en DOM antes de pasar al siguiente. Pero AFAIK ngRepeat no informa cuando termina de "repetir", por lo que va a ser feo.

Otra opción es dividir los datos en el servidor en páginas y buscarlos en varias solicitudes, pero eso es aún más feo.

Miré a través de la documentación angular en busca de algo como ng-repeat="data in dataset" ng-repeat-steps="500" , pero no encontré nada. Soy bastante nuevo en las formas angulares, por lo que es posible que me esté faltando el punto por completo. ¿Cuáles son las mejores prácticas en esto?


Además de todas las pistas anteriores como track by y loops más pequeños, este también me ayudó mucho

<span ng-bind="::stock.name"></span>

este pedazo de código imprimirá el nombre una vez que se haya cargado, y dejará de verlo después de eso. Del mismo modo, para ng-repeats, podría usarse como

<div ng-repeat="stock in ::ctrl.stocks">{{::stock.name}}</div>

sin embargo, solo funciona para AngularJS versión 1.3 y superior. De http://www.befundoo.com/blog/optimizing-ng-repeat-in-angularjs/


El enfoque más novedoso (y posiblemente el más escalable) para superar estos desafíos con grandes conjuntos de datos se materializa en el enfoque de la directiva collectionRepeat de Ionic y de otras implementaciones como esta. Un término sofisticado para esto es ''oclusión selectiva'' , pero puede resumirlo así: no limite el recuento de los elementos DOM renderizados a un número paginado arbitrario (pero aún alto) como 50, 100, 500 ... en su lugar , limite solo a tantos elementos como el usuario pueda ver .

Si haces algo así como lo que comúnmente se conoce como "desplazamiento infinito", estás reduciendo el conteo de DOM inicial un poco, pero se hincha rápidamente después de que un par se actualiza, porque todos esos elementos nuevos están simplemente añadidos en la parte inferior. El desplazamiento se convierte en un rastreo, porque el desplazamiento se trata de contar elementos. No hay nada infinito al respecto.

Mientras que, el enfoque collectionRepeat es usar solo tantos elementos como quepan en la ventana gráfica, y luego reciclarlos . Cuando un elemento gira fuera de la vista, se separa del árbol de renderizado, se rellena con datos para un nuevo elemento en la lista y luego se vuelve a unir al árbol de renderizado en el otro extremo de la lista. Esta es la forma más rápida conocida por el hombre para obtener nueva información dentro y fuera del DOM, haciendo uso de un conjunto limitado de elementos existentes, en lugar del ciclo tradicional de crear / destruir ... crear / destruir. Usando este enfoque, puedes implementar verdaderamente un desplazamiento infinito .

Tenga en cuenta que no tiene que usar Ionic para usar / hackear / adaptar collectionRepeat , o cualquier otra herramienta como esta. Es por eso que lo llaman de código abierto. :-) (Dicho esto, el equipo iónico está haciendo algunas cosas bastante ingeniosas, dignas de su atención).

Hay al menos un excelente ejemplo de hacer algo muy similar en React. Solo que en lugar de reciclar los elementos con contenido actualizado, simplemente eliges no renderizar nada en el árbol que no esté a la vista. Es increíblemente rápido en 5000 artículos, aunque su implementación POC muy simple permite un poco de parpadeo ...

Además ... para hacer eco de algunas de las otras publicaciones, usar track by es de gran ayuda, incluso con conjuntos de datos más pequeños. Considera que es obligatorio.


Estoy de acuerdo con @ AndreM96 en que el mejor enfoque es mostrar solo una cantidad limitada de filas, un UX más rápido y mejor, esto podría hacerse con una paginación o con un desplazamiento infinito.

El desplazamiento infinito con Angular es realmente simple con el filtro limitTo . Solo tienes que establecer el límite inicial y cuando el usuario solicita más datos (estoy usando un botón para simplificar) aumentas el límite.

<table> <tr ng-repeat="d in data | limitTo:totalDisplayed"><td>{{d}}</td></tr> </table> <button class="btn" ng-click="loadMore()">Load more</button> //the controller $scope.totalDisplayed = 20; $scope.loadMore = function () { $scope.totalDisplayed += 20; }; $scope.data = data;

Aquí hay un JsBin .

Este enfoque podría ser un problema para los teléfonos, porque generalmente se retrasan al desplazarse por una gran cantidad de datos, por lo que en este caso creo que una paginación se ajusta mejor.

Para hacerlo necesitará el filtro limitTo y también un filtro personalizado para definir el punto de inicio de los datos que se muestran.

Aquí hay un JSBin con una paginación.


Otra versión @Steffomio

En lugar de agregar cada elemento individualmente, podemos agregar elementos por partes.

// chunks function from here: // http://.com/questions/8495687/split-array-into-chunks#11764168 var chunks = chunk(folders, 100); //immediate display of our first set of items $scope.items = chunks[0]; var delay = 100; angular.forEach(chunks, function(value, index) { delay += 100; // skip the first chuck if( index > 0 ) { $timeout(function() { Array.prototype.push.apply($scope.items,value); }, delay); } });



Recomiendo ver esto:

Optimización de AngularJS: 1200 ms a 35 ms

hicieron una nueva directiva optimizando ng-repeat en 4 partes:

Optimización # 1: elementos DOM de caché

Optimización # 2: observadores agregados

Optimización n.º 3: aplazar la creación de elementos

Optimización # 4: observadores de desviación para elementos ocultos

el proyecto está aquí en github:

Uso:

1- Incluye estos archivos en tu aplicación de una sola página:

  • core.js
  • scalyr.js
  • slyEvaluate.js
  • slyRepeat.js

2- añadir dependencia de módulo:

var app = angular.module("app", [''sly'']);

3- reemplazar ng-repeat

<tr sly-repeat="m in rows"> .....<tr>

¡Disfrutar!


Regla n. ° 1: nunca permita que el usuario espere nada.

Teniendo esto en cuenta, una página que crece en la vida y necesita 10 segundos parece mucho más rápida que esperar 3 segundos antes de una pantalla en blanco y obtener todo de una vez.

Entonces, en lugar de hacer que la página sea rápida, simplemente deje que la página parezca rápida, incluso si el resultado final es más lento:

function applyItemlist(items){ var item = items.shift(); if(item){ $timeout(function(){ $scope.items.push(item); applyItemlist(items); }, 0); // <-- try a little gap of 10ms } }

El código de arriba muestra la lista para crecer fila por fila, y siempre es más lento que renderizar todos a la vez. Pero para el usuario parece ser más rápido.



para el conjunto de datos grande y el menú desplegable de valor múltiple, es mejor utilizar ng-options lugar de ng-repeat .

ng-repeat es lento porque gira sobre todos los valores venideros, pero ng-options simplemente se muestran en la opción de selección.

ng-options=''state.StateCode as state.StateName for state in States''>

mucho más rápido que

<option ng-repeat="state in States" value="{{state.StateCode}}"> {{state.StateName }} </option>


El desplazamiento virtual es otra forma de mejorar el rendimiento de desplazamiento cuando se trata de grandes listas y grandes conjuntos de datos.

Una forma de implementar esto es mediante el uso de material angular md-virtual-repeat como se demuestra en esta demostración con 50,000 elementos

Tomado directamente de la documentación de repetición virtual:

La repetición virtual es un sustituto limitado para ng-repeat que representa solo nodos dom suficientes para llenar el contenedor y reciclarlos a medida que el usuario se desplaza.


Created a directive (ng-repeat with lazy loading)

que carga datos cuando llega al final de la página y elimina la mitad de los datos cargados anteriormente y cuando llega a la parte superior del div nuevamente los datos anteriores (dependiendo del número de página) se cargarán eliminando la mitad de los datos actuales. Así en DOM a la vez, solo existen datos limitados que pueden conducir a un mejor rendimiento en lugar de presentar datos completos en carga.

CÓDIGO HTML:

<!DOCTYPE html> <html ng-app="plunker"> <head> <meta charset="utf-8" /> <title>AngularJS Plunker</title> <script>document.write(''<base href="'' + document.location + ''" />'');</script> <link rel="stylesheet" href="style.css" /> <script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script> <script data-require="[email protected]" src="https://code.angularjs.org/1.3.20/angular.js" data-semver="1.3.20"></script> <script src="app.js"></script> </head> <body ng-controller="ListController"> <div class="row customScroll" id="customTable" datafilter pagenumber="pageNumber" data="rowData" searchdata="searchdata" itemsPerPage="{{itemsPerPage}}" totaldata="totalData" selectedrow="onRowSelected(row,row.index)" style="height:300px;overflow-y: auto;padding-top: 5px"> <!--<div class="col-md-12 col-xs-12 col-sm-12 assign-list" ng-repeat="row in CRGC.rowData track by $index | orderBy:sortField:sortReverse | filter:searchFish">--> <div class="col-md-12 col-xs-12 col-sm-12 pdl0 assign-list" style="padding:10px" ng-repeat="row in rowData" ng-hide="row[CRGC.columns[0].id]=='''' && row[CRGC.columns[1].id]==''''"> <!--col1--> <div ng-click ="onRowSelected(row,row.index)"> <span>{{row["sno"]}}</span> <span>{{row["id"]}}</span> <span>{{row["name"]}}</span></div> <!-- <div class="border_opacity"></div> --> </div> </div> </body> </html>

CÓDIGO ANGULAR:

var app = angular.module(''plunker'', []); var x; ListController.$inject = [''$scope'', ''$timeout'', ''$q'', ''$templateCache'']; function ListController($scope, $timeout, $q, $templateCache) { $scope.itemsPerPage = 40; $scope.lastPage = 0; $scope.maxPage = 100; $scope.data = []; $scope.pageNumber = 0; $scope.makeid = function() { var text = ""; var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; for (var i = 0; i < 5; i++) text += possible.charAt(Math.floor(Math.random() * possible.length)); return text; } $scope.DataFormFunction = function() { var arrayObj = []; for (var i = 0; i < $scope.itemsPerPage*$scope.maxPage; i++) { arrayObj.push({ sno: i + 1, id: Math.random() * 100, name: $scope.makeid() }); } $scope.totalData = arrayObj; $scope.totalData = $scope.totalData.filter(function(a,i){ a.index = i; return true; }) $scope.rowData = $scope.totalData.slice(0, $scope.itemsperpage); } $scope.DataFormFunction(); $scope.onRowSelected = function(row,index){ console.log(row,index); } } angular.module(''plunker'').controller(''ListController'', ListController).directive(''datafilter'', function($compile) { return { restrict: ''EAC'', scope: { data: ''='', totalData: ''=totaldata'', pageNumber: ''=pagenumber'', searchdata: ''='', defaultinput: ''='', selectedrow: ''&'', filterflag: ''='', totalFilterData: ''='' }, link: function(scope, elem, attr) { //scope.pageNumber = 0; var tempData = angular.copy(scope.totalData); scope.totalPageLength = Math.ceil(scope.totalData.length / +attr.itemsperpage); console.log(scope.totalData); scope.data = scope.totalData.slice(0, attr.itemsperpage); elem.on(''scroll'', function(event) { event.preventDefault(); // var scrollHeight = angular.element(''#customTable'').scrollTop(); var scrollHeight = document.getElementById("customTable").scrollTop /*if(scope.filterflag && scope.pageNumber != 0){ scope.data = scope.totalFilterData; scope.pageNumber = 0; angular.element(''#customTable'').scrollTop(0); }*/ if (scrollHeight < 100) { if (!scope.filterflag) { scope.scrollUp(); } } if (angular.element(this).scrollTop() + angular.element(this).innerHeight() >= angular.element(this)[0].scrollHeight) { console.log("scroll bottom reached"); if (!scope.filterflag) { scope.scrollDown(); } } scope.$apply(scope.data); }); /* * Scroll down data append function */ scope.scrollDown = function() { if (scope.defaultinput == undefined || scope.defaultinput == "") { //filter data append condition on scroll scope.totalDataCompare = scope.totalData; } else { scope.totalDataCompare = scope.totalFilterData; } scope.totalPageLength = Math.ceil(scope.totalDataCompare.length / +attr.itemsperpage); if (scope.pageNumber < scope.totalPageLength - 1) { scope.pageNumber++; scope.lastaddedData = scope.totalDataCompare.slice(scope.pageNumber * attr.itemsperpage, (+attr.itemsperpage) + (+scope.pageNumber * attr.itemsperpage)); scope.data = scope.totalDataCompare.slice(scope.pageNumber * attr.itemsperpage - 0.5 * (+attr.itemsperpage), scope.pageNumber * attr.itemsperpage); scope.data = scope.data.concat(scope.lastaddedData); scope.$apply(scope.data); if (scope.pageNumber < scope.totalPageLength) { var divHeight = $(''.assign-list'').outerHeight(); if (!scope.moveToPositionFlag) { angular.element(''#customTable'').scrollTop(divHeight * 0.5 * (+attr.itemsperpage)); } else { scope.moveToPositionFlag = false; } } } } /* * Scroll up data append function */ scope.scrollUp = function() { if (scope.defaultinput == undefined || scope.defaultinput == "") { //filter data append condition on scroll scope.totalDataCompare = scope.totalData; } else { scope.totalDataCompare = scope.totalFilterData; } scope.totalPageLength = Math.ceil(scope.totalDataCompare.length / +attr.itemsperpage); if (scope.pageNumber > 0) { this.positionData = scope.data[0]; scope.data = scope.totalDataCompare.slice(scope.pageNumber * attr.itemsperpage - 0.5 * (+attr.itemsperpage), scope.pageNumber * attr.itemsperpage); var position = +attr.itemsperpage * scope.pageNumber - 1.5 * (+attr.itemsperpage); if (position < 0) { position = 0; } scope.TopAddData = scope.totalDataCompare.slice(position, (+attr.itemsperpage) + position); scope.pageNumber--; var divHeight = $(''.assign-list'').outerHeight(); if (position != 0) { scope.data = scope.TopAddData.concat(scope.data); scope.$apply(scope.data); angular.element(''#customTable'').scrollTop(divHeight * 1 * (+attr.itemsperpage)); } else { scope.data = scope.TopAddData; scope.$apply(scope.data); angular.element(''#customTable'').scrollTop(divHeight * 0.5 * (+attr.itemsperpage)); } } } } }; });

Demostración con directiva

Another Solution: If you using UI-grid in the project then same implementation is there in UI grid with infinite-scroll.

Dependiendo de la altura de la división, carga los datos y luego de desplazarse, se agregarán nuevos datos y se eliminarán los datos anteriores.

Código HTML:

<!DOCTYPE html> <html ng-app="plunker"> <head> <meta charset="utf-8" /> <title>AngularJS Plunker</title> <script>document.write(''<base href="'' + document.location + ''" />'');</script> <link rel="stylesheet" href="style.css" /> <link rel="stylesheet" href="https://cdn.rawgit.com/angular-ui/bower-ui-grid/master/ui-grid.min.css" type="text/css" /> <script data-require="[email protected]" src="https://code.angularjs.org/1.3.20/angular.js" data-semver="1.3.20"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-grid/4.0.6/ui-grid.js"></script> <script src="app.js"></script> </head> <body ng-controller="ListController"> <div class="input-group" style="margin-bottom: 15px"> <div class="input-group-btn"> <button class=''btn btn-primary'' ng-click="resetList()">RESET</button> </div> <input class="form-control" ng-model="search" ng-change="abc()"> </div> <div data-ui-grid="gridOptions" class="grid" ui-grid-selection data-ui-grid-infinite-scroll style="height :400px"></div> <button ng-click="getProductList()">Submit</button> </body> </html>

Código Angular:

var app = angular.module(''plunker'', [''ui.grid'', ''ui.grid.infiniteScroll'', ''ui.grid.selection'']); var x; angular.module(''plunker'').controller(''ListController'', ListController); ListController.$inject = [''$scope'', ''$timeout'', ''$q'', ''$templateCache'']; function ListController($scope, $timeout, $q, $templateCache) { $scope.itemsPerPage = 200; $scope.lastPage = 0; $scope.maxPage = 5; $scope.data = []; var request = { "startAt": "1", "noOfRecords": $scope.itemsPerPage }; $templateCache.put(''ui-grid/selectionRowHeaderButtons'', "<div class=/"ui-grid-selection-row-header-buttons /" ng-class=/"{''ui-grid-row-selected'': row.isSelected}/" ><input style=/"margin: 0; vertical-align: middle/" type=/"checkbox/" ng-model=/"row.isSelected/" ng-click=/"row.isSelected=!row.isSelected;selectButtonClick(row, $event)/">&nbsp;</div>" ); $templateCache.put(''ui-grid/selectionSelectAllButtons'', "<div class=/"ui-grid-selection-row-header-buttons /" ng-class=/"{''ui-grid-all-selected'': grid.selection.selectAll}/" ng-if=/"grid.options.enableSelectAll/"><input style=/"margin: 0; vertical-align: middle/" type=/"checkbox/" ng-model=/"grid.selection.selectAll/" ng-click=/"grid.selection.selectAll=!grid.selection.selectAll;headerButtonClick($event)/"></div>" ); $scope.gridOptions = { infiniteScrollDown: true, enableSorting: false, enableRowSelection: true, enableSelectAll: true, //enableFullRowSelection: true, columnDefs: [{ field: ''sno'', name: ''sno'' }, { field: ''id'', name: ''ID'' }, { field: ''name'', name: ''My Name'' }], data: ''data'', onRegisterApi: function(gridApi) { gridApi.infiniteScroll.on.needLoadMoreData($scope, $scope.loadMoreData); $scope.gridApi = gridApi; } }; $scope.gridOptions.multiSelect = true; $scope.makeid = function() { var text = ""; var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; for (var i = 0; i < 5; i++) text += possible.charAt(Math.floor(Math.random() * possible.length)); return text; } $scope.abc = function() { var a = $scope.search; x = $scope.searchData; $scope.data = x.filter(function(arr, y) { return arr.name.indexOf(a) > -1 }) console.log($scope.data); if ($scope.gridApi.grid.selection.selectAll) $timeout(function() { $scope.gridApi.selection.selectAllRows(); }, 100); } $scope.loadMoreData = function() { var promise = $q.defer(); if ($scope.lastPage < $scope.maxPage) { $timeout(function() { var arrayObj = []; for (var i = 0; i < $scope.itemsPerPage; i++) { arrayObj.push({ sno: i + 1, id: Math.random() * 100, name: $scope.makeid() }); } if (!$scope.search) { $scope.lastPage++; $scope.data = $scope.data.concat(arrayObj); $scope.gridApi.infiniteScroll.dataLoaded(); console.log($scope.data); $scope.searchData = $scope.data; // $scope.data = $scope.searchData; promise.resolve(); if ($scope.gridApi.grid.selection.selectAll) $timeout(function() { $scope.gridApi.selection.selectAllRows(); }, 100); } }, Math.random() * 1000); } else { $scope.gridApi.infiniteScroll.dataLoaded(); promise.resolve(); } return promise.promise; }; $scope.loadMoreData(); $scope.getProductList = function() { if ($scope.gridApi.selection.getSelectedRows().length > 0) { $scope.gridOptions.data = $scope.resultSimulatedData; $scope.mySelectedRows = $scope.gridApi.selection.getSelectedRows(); //<--Property undefined error here console.log($scope.mySelectedRows); //alert(''Selected Row: '' + $scope.mySelectedRows[0].id + '', '' + $scope.mySelectedRows[0].name + ''.''); } else { alert(''Select a row first''); } } $scope.getSelectedRows = function() { $scope.mySelectedRows = $scope.gridApi.selection.getSelectedRows(); } $scope.headerButtonClick = function() { $scope.selectAll = $scope.grid.selection.selectAll; } }

Demostración con cuadrícula de UI con demostración de desplazamiento infinito