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

165

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

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

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

Miré a través de la documentación angular en busca de algo así 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é perdiendo el punto por completo. ¿Cuáles son las mejores prácticas en esto?

n1313
fuente
10
¿Realmente quieres mostrar TODAS las filas? ¿Qué hay de mostrar solo las filas que puede ver el usuario? por ejemplo, podría usar limitTopara mostrar solo 20 elementos: <p ng-repeat="data in dataset | limitTo:20">{{data}}</p>esto muestra solo 20 elementos. Luego, podría usar páginas y mostrar los siguientes 10 elementos o algo así. :)
AndreM96
para ese "informe cuando termine de" repetirse "", podría usar una directiva personalizada además de ng-repeat. (vea aquí la respuesta seleccionada) stackoverflow.com/questions/13471129/…
mayankcpdixit
refiérase a esta pregunta seguramente lo ayudará. [ingrese la descripción del enlace aquí] [1] [1]: stackoverflow.com/questions/25481021/…
Mahesh

Respuestas:

159

Estoy de acuerdo con @ AndreM96 en que el mejor enfoque es mostrar solo una cantidad limitada de filas, 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 limitTo filter. Solo tiene que establecer el límite inicial y cuando el usuario solicita más datos (estoy usando un botón para simplificar), incrementa 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 desplazar una gran cantidad de datos, por lo que en este caso creo que una paginación encaja mejor.

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

Aquí hay un JSBin con una paginación.

Bertrand
fuente
Buena alternativa !!! Conoces cualquier método a utilizar si estoy obligado a mostrar todos los elementos. ¿Alguna señal de carga o una después de una inserción en DOM o algo así?
mayankcpdixit
¿Te refieres a mostrar un "cargando ..." o algo mientras se recuperan los datos?
Bertrand
1
@Sumit limitTo se aplicará al alcance ng-repeat, por lo que el resultado será una nueva matriz que se pasará a ng-repeat, su matriz de datos seguirá siendo la misma y aún puede buscar todo el contenido.
Bertrand
12
Si el usuario presiona cargar más 10 veces y cada vez que presiona agrega 100 elementos más, ¿cómo puede esto mejorar el rendimiento?
hariszaman
55
@hariszaman Estoy de acuerdo. Esto no mejora el rendimiento. Simplemente retrasa el bajo rendimiento. El desplazamiento infinito lo meterá en problemas a menos que lo esté virtualizando (lo que hace ui-grid).
Richard
41

El enfoque más popular, 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 en otras implementaciones similares. Un término elegante para esto es 'eliminación de oclusiones' , pero puede resumirlo como: no limite el recuento de elementos DOM representados a un número arbitrario (pero aún alto) paginado como 50, 100, 500 ... en su lugar , limite solo a tantos elementos como el usuario pueda ver .

Si hace algo como lo que comúnmente se conoce como "desplazamiento infinito", está reduciendo un poco el conteo inicial de DOM, pero se hincha rápidamente después de un par de actualizaciones, porque todos esos elementos nuevos se agregan en la parte inferior. El desplazamiento se arrastra, porque el desplazamiento se trata de contar elementos. No hay nada infinito al respecto.

Mientras que, el collectionRepeatenfoque es usar solo tantos elementos como quepan en la ventana gráfica, y luego reciclarlos . A medida que un elemento gira fuera de la vista, se separa del árbol de representación, se vuelve a llenar con datos para un nuevo elemento en la lista y luego se vuelve a unir al árbol de representación 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. Con este enfoque, realmente puede implementar un desplazamiento infinito .

Tenga en cuenta que no tiene que usar Ionic para usar / piratear / adaptar collectionRepeat, ni ninguna otra herramienta como esta. Por eso lo llaman de código abierto. :-) (Dicho esto, el equipo de Ionic 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 elige no representar 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, el uso track byes muy útil, incluso con conjuntos de datos más pequeños. Considéralo obligatorio.

XML
fuente
Impresionante idea del equipo de Ionic. Me pregunto si eso vino de cómo se representan las vistas nativas.
Bradley Flood
Por ejemplo, UITableView en iOS utiliza el mismo enfoque para representar grandes conjuntos de datos. Creo que este es un enfoque común utilizado en muchas vistas nativas.
Dmitry Kotenko
36

Recomiendo ver esto:

Optimizando AngularJS: 1200ms a 35ms

hicieron una nueva directiva al optimizar ng-repeat en 4 partes:

Optimización # 1: elementos DOM de caché

Optimización # 2: Observadores agregados

Optimización # 3: aplazar la creación de elementos

Optimización # 4: evitar los observadores 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- agregar dependencia de módulo:

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

3- reemplazar ng-repeat

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

¡Disfrutar!

pixparker
fuente
44
Creo que este scalyr.js ya incluye los otros archivos. Porque es el resultado del script de compilación.
dnocode
Intenté usar Scalyr pero el filtro no funciona. <tr sly-repeat = "opción en main.customers | filter: search_input | limitTo: 20">
aldesabido
Esto es extremadamente útil. Lo estoy usando en una aplicación AngularJS 1.6 donde el cliente quiere ver muchas celdas de datos (normalmente diseño formularios con elementos de paginación / datos reducidos, pero el cliente necesita comparar muchos datos a la vez). Hasta ahora, la cuadrícula de celdas ha pasado de inutilizable a perfectamente fina debido a esta biblioteca. Pero esta lib fue escrita en los 1.2 días de AngularJS, por lo que probaré cuidadosamente buscando problemas.
folleto
Por lo que puedo decir en este momento, el archivo gatedScope.js (323 líneas) es el único que debe verificarse para que se pueda ejecutar en versiones más actuales de AngularJS. Esta solicitud de extracción es notable: github.com/karser/angular/commit/… . Actualiza la firma rootScope. $ New.
folleto
incluyó los cuatro archivos js y los usé, sly-repeatpero nada me ayudó, los resultados aún son lentos y los retrasos del navegador también reciben violaciones [Violation] 'setTimeout' handler took 54ms,[Violation] 'scroll' handler took 1298ms
Gaurav Aggarwal
15

Además de todos los consejos anteriores, como el seguimiento y los bucles más pequeños, este también me ayudó mucho

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

este 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/

Shilan
fuente
¿Necesitas tanto la ::repetición como la expresión? Los documentos dicen lo contrario, pero no estoy seguro de cómo probar que esto está funcionando. docs.angularjs.org/guide/expression
Crhistian Ramirez
12

Puede usar "seguimiento por" para aumentar el rendimiento:

<div ng-repeat="a in arr track by a.trackingKey">

Más rápido que:

<div ng-repeat="a in arr">

ref: https://www.airpair.com/angularjs/posts/angularjs-performance-large-applications

usuario1920302
fuente
1
Esto realmente no ayuda para el rendimiento. Ver jsperf.com/ng-repeat-vs-ng-repeat-with-trace-by-id
ilter
con seguimiento por, no realiza un seguimiento del elemento de matriz desde el principio cada vez que obtiene datos nuevos. Como resultado, esto mejora el rendimiento.
user1920302
2
Esto es útil solo cuando los datos en ng-repeat cambian. Para la carga inicial, podría no crear una mejora en el rendimiento.
Sumesh Kuttan
11

Si todas sus filas tienen la misma altura, definitivamente debería echar un vistazo a la virtualización ng-repeat: http://kamilkp.github.io/angular-vs-repeat/

Esta demostración parece muy prometedora (y admite desplazamiento inercial)

bartekp
fuente
2
El rendimiento de desplazamiento en dispositivos móviles no es aceptable (los eventos de desplazamiento no se activan en iOS móvil (solo desde 8)
Johny
9

Regla No.1: Nunca permita que el usuario espere nada.

Teniendo en cuenta que una página de crecimiento vital que necesita 10 segundos aparece mucho más rápido que esperar 3 segundos antes de una pantalla en blanco y obtener todo a la 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 anterior permite que la lista crezca fila por fila, y siempre es más lenta que renderizar de una vez. Pero para el usuario parece ser más rápido.

Steffomio
fuente
¿Cómo usar esta función en la página html?
Antonis
9

Desplazamiento virtual es otra forma de mejorar el rendimiento del 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 artículos

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

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

Sarantis Tofas
fuente
2
Wow, creo que esta es la respuesta más interesante. ¿Funcionará esto con una versión anterior de angular? (por ejemplo, ver 1.2)
Thariq Nugrohotomo
2
@ThariqNugrohotomo Tenga en cuenta que el uso de material angular requiere el uso de Angular 1.3.xo superior. También gracias por el soporte, también estoy realmente sorprendido por la repetición virtual y ya la estamos usando en una aplicación móvil que muestra una lista muy larga de resultados.
Sarantis Tofas
6

Otra versión @Steffomio

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

// chunks function from here: 
// http://stackoverflow.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);
    }       
});
Luevano
fuente
Idea interesante. Intenté esto en una matriz de ~ 8000 elementos, y si bien inicialmente hizo que la página respondiera mejor, se volvió menos receptiva después de cada fragmento.
Paul Brannan
Este fue un gran problema en mi aplicación después de tener más de 500 elementos. Sugiero paginación o carga infinita en su lugar.
joalcego
0

A veces, lo que sucedió, 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 (supongamos que tarda 900 ms en monitor).

Entonces, lo que está sucediendo aquí es de 800 ms. Solo se necesita para mostrar la página web.

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

Por lo tanto, no cargaré renderizar todos los datos a la vez, solo 50 datos que estoy cargando inicialmente, lo que toma solo 50 ms (supongo que aquí).

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

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

UniCoder
fuente
0
Created a directive (ng-repeat with lazy loading) 

que carga los datos cuando llega al final de la página y elimina la mitad de los datos cargados previamente 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. a la vez, solo hay datos limitados, lo que puede conducir a un mejor rendimiento en lugar de representar 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));
            }
          }
        }
    }
  };
});

Demo 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, al 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 IU con demostración de desplazamiento infinito

ankesh jain
fuente
Un enlace a una solución es bienvenido, pero asegúrese de que su respuesta sea útil sin él: agregue contexto alrededor del enlace para que sus otros usuarios tengan una idea de qué es y por qué está allí, luego cite la parte más relevante de la página ' volver a vincular en caso de que la página de destino no esté disponible. Se pueden eliminar las respuestas que son poco más que un enlace .
Sᴀᴍ Elᴇᴌᴀ
-2

para el gran conjunto de datos y la caída de valor múltiple hacia abajo, es mejor utilizar ng-optionsen lugar de ng-repeat.

ng-repeates lento porque recorre todos los valores que vienen, pero ng-optionssimplemente se muestra 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>
Ghebrehiywet
fuente
¿Verificaste el rendimiento de ng-options? Estoy tratando de optimizar mi código y no me ayudó. La velocidad es la misma que ng-repeat. -1
Icet
solo funciona para select, ng-repeat es mucho más poderoso. Sin embargo, es cierto que ng-Options es mucho más rápido que ng-repeat. Los documentos de AngularJs mencionan 2000 elementos por diferencias: docs.angularjs.org/api/ng/directive/select
kaiser