Como melhorar o desempenho do ngRepeat em um grande conjunto de dados (angular.js)?

165

Eu tenho um enorme conjunto de dados de vários milhares de linhas com cerca de 10 campos cada, cerca de 2 MB de dados. Eu preciso exibi-lo no navegador. A abordagem mais direta (buscar dados, inseri-los $scope, deixarng-repeat="" seu trabalho) funciona bem, mas congela o navegador por cerca de meio minuto quando começa a inserir nós no DOM. Como devo abordar esse problema?

Uma opção é anexar linhas para $scopeincrementalmente e aguardarngRepeat concluir a inserção de um pedaço no DOM antes de passar para o próximo. Mas o AFAIK ngRepeat não reporta quando termina a "repetição", por isso será feio.

Outra opção é dividir os dados no servidor em páginas e buscá-los em várias solicitações, mas isso é ainda mais feio.

Examinei a documentação do Angular em busca de algo parecido ng-repeat="data in dataset" ng-repeat-steps="500", mas não encontrei nada. Eu sou bastante novo nas maneiras angulares, por isso é possível que eu esteja completamente errado. Quais são as melhores práticas para isso?

n1313
fonte
10
Deseja realmente exibir TODAS as linhas? Que tal exibir apenas o número de linhas que o usuário pode ver. por exemplo, você pode usar limitTopara exibir apenas 20 itens: <p ng-repeat="data in dataset | limitTo:20">{{data}}</p>mostra apenas 20 itens. Então você pode usar as páginas e mostrar os próximos 10 itens ou algo assim. :)
AndreM96
para essa coisa de "relatar quando terminar 'repetir'", você poderia usar uma diretiva personalizada além do ng-repeat. (veja aqui a resposta selecionada) stackoverflow.com/questions/13471129/…
mayankcpdixit
remeter esta pergunta certamente o ajudará. [insira a descrição do link aqui] [1] [1]: stackoverflow.com/questions/25481021/…
Mahesh

Respostas:

159

Concordo com o @ AndreM96 que a melhor abordagem é exibir apenas uma quantidade limitada de linhas, UX mais rápida e melhor, isso pode ser feito com uma paginação ou com um rolagem infinita.

Rolagem infinita com Angular é realmente simples com filtro limitTo . Você só precisa definir o limite inicial e, quando o usuário solicitar mais dados (estou usando um botão para simplificar), você aumenta o limite.

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

Aqui está um JsBin .

Essa abordagem pode ser um problema para os telefones, porque geralmente eles ficam atrasados ​​ao rolar muitos dados; portanto, nesse caso, acho que uma paginação se encaixa melhor.

Para isso, será necessário o filtro limitTo e também um filtro personalizado para definir o ponto inicial dos dados que estão sendo exibidos.

Aqui está um JSBin com uma paginação.

Bertrand
fonte
Boa alternativa !!! Você conhece algum método a ser utilizado se eu estiver obrigado a mostrar todos os itens. algum sinal de carregamento ou um após uma inserção no DOM ou algo assim?
mayankcpdixit
Você quer dizer exibir um "carregamento ..." ou algo enquanto os dados estão sendo buscados?
Bertrand
1
O @Sumit limitTo será aplicado ao escopo ng-repeat, portanto o resultado será uma nova matriz que será passada para ng-repeat, sua matriz de dados continuará a mesma e você ainda poderá pesquisar todo o conteúdo.
Bertrand
12
se o usuário pressionar carregar mais 10 vezes e toda impressora adicionar mais 100 itens, como isso pode melhorar o desempenho?
hariszaman
5
@hariszaman Eu concordo. Isso não melhora o desempenho. Isso apenas atrasa o desempenho ruim. Rolagem infinita causará problemas, a menos que você a esteja virtualizando (o que a interface do usuário faz).
Richard
41

A abordagem mais quente - e sem dúvida a mais escalável - para superar esses desafios com grandes conjuntos de dados é incorporada pela abordagem da diretiva collectionRepeat da Ionic e por outras implementações semelhantes. Um termo chique para isso é 'seleção de oclusão' , mas você pode resumir como: não basta limitar a contagem de elementos DOM renderizados a um número paginado arbitrário (mas ainda alto) como 50, 100, 500 ... em vez disso , limite apenas a quantos elementos o usuário puder ver .

Se você fizer algo como o que é comumente conhecido como "rolagem infinita", estará reduzindo um pouco a contagem inicial do DOM, mas ela inchará rapidamente depois de algumas atualizações, porque todos esses novos elementos estão apenas na parte inferior. A rolagem chega a um rastreamento, porque a rolagem tem tudo a ver com a contagem de elementos. Não há nada de infinito nisso.

Visto que a collectionRepeatabordagem é usar apenas quantos elementos caberem na janela de exibição e depois reciclá-los . Como um elemento gira fora da vista, ele é desanexado da árvore de renderização, reabastecido com dados para um novo item na lista e, em seguida, reconectado à árvore de renderização na outra extremidade da lista. Esta é a maneira mais rápida conhecida pelo homem de obter e retirar novas informações do DOM, utilizando um conjunto limitado de elementos existentes, em vez do ciclo tradicional de criar / destruir ... criar / destruir. Usando essa abordagem, você pode realmente implementar uma rolagem infinita .

Observe que você não precisa usar o Ionic para usar / hack / adaptar collectionRepeat, ou qualquer outra ferramenta semelhante. É por isso que eles chamam de código aberto. :-) (Dito isto, a equipe jônica está fazendo algumas coisas bastante engenhosas, dignas de sua atenção.)


Há pelo menos um excelente exemplo de fazer algo muito semelhante no React. Somente em vez de reciclar os elementos com conteúdo atualizado, você simplesmente escolhe não renderizar nada na árvore que não esteja à vista. É rápido em 5000 itens, embora sua implementação muito simples de POC permita um pouco de cintilação ...


Também ... para repetir algumas das outras postagens, o uso track byé muito útil, mesmo com conjuntos de dados menores. Considere obrigatório.

XML
fonte
Idéia impressionante pela equipe Ionic. Gostaria de saber se isso veio de como as visualizações nativas são renderizadas?
Bradley Flood
Por exemplo, o UITableView no iOS usa a mesma abordagem para renderizar grandes conjuntos de dados. Eu acho que essa é uma abordagem comum usada em muitas visões nativas.
Dmitry Kotenko
36

Eu recomendo ver isso:

Otimizando o AngularJS: 1200ms a 35ms

eles fizeram uma nova diretiva otimizando o ng-repeat em 4 partes:

Otimização nº 1: elementos DOM do cache

Otimização nº 2: agregadores de observadores

Otimização nº 3: adiar a criação do elemento

Otimização nº 4: ignorar observadores para elementos ocultos

o projeto está aqui no github:

Uso:

1- inclua esses arquivos no seu aplicativo de página única:

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

2- adicione dependência do módulo:

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

3- substituir ng-repeat

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

Desfrutar!

pixparker
fonte
4
Eu acho que este scalyr.js já inclui os outros arquivos. Porque é o resultado do script de construção.
dnocode 3/02/16
Eu tentei usar Scalyr, mas o filtro não funciona. <tr sly-repeat = "opção em main.customers | filter: search_input | limitTo: 20">
aldesabido
Isso é extremamente útil. Estou usando-o em um aplicativo AngularJS 1.6, em que o cliente deseja ver muitas células de dados (normalmente, desenvolvo formulários com paginação / elementos de dados reduzidos, mas o cliente precisa comparar muitos dados de uma só vez). Até agora, a grade de células passou de inutilizável para perfeitamente fina por causa dessa biblioteca. Mas essa lib foi escrita nos AngularJS 1.2 dias, então testarei cuidadosamente procurando problemas.
Panfleto
Pelo que sei neste momento, o arquivo gatedScope.js (323 linhas) é o único que precisa ser verificado para ser executável nas versões mais atuais do AngularJS. Essa solicitação de recebimento é notável: github.com/karser/angular/commit/… . Ele atualiza a assinatura rootScope. $ New.
insecto
incluídos todos os quatro arquivos js e usado sly-repeat, mas nada me ajudou os resultados ainda são lentos e defasagens navegador também recebendo violações [Violation] 'setTimeout' handler took 54ms,[Violation] 'scroll' handler took 1298ms
Gaurav Aggarwal
15

Além de todas as dicas acima, como rastrear e loops menores, este também me ajudou muito

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

esse trecho de código imprimiria o nome depois de carregado e deixaria de observá-lo depois disso. Da mesma forma, para repetições ng, poderia ser usado como

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

no entanto, ele funciona apenas para o AngularJS versão 1.3 e superior. Em http://www.befundoo.com/blog/optimizing-ng-repeat-in-angularjs/

Shilan
fonte
Você precisa ::repetir e também a expressão? Os documentos dizem o contrário, mas não tenho certeza de como testar se isso está funcionando. docs.angularjs.org/guide/expression
Crhistian Ramirez
12

Você pode usar "rastrear por" para aumentar o desempenho:

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

Mais rápido que:

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

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

user1920302
fonte
1
Isso realmente não ajuda no desempenho. Veja jsperf.com/ng-repeat-vs-ng-repeat-with-trace-by-id
ilter
com track by você não controla o elemento da matriz desde o início sempre que obtém novos dados. Como resultado, isso melhora o desempenho.
precisa saber é o seguinte
2
Isso é útil apenas quando os dados no ng-repeat são alterados. Para carregamento inicial, ele pode não criar uma melhoria de desempenho.
Sumesh Kuttan
11

Se todas as suas linhas tiverem altura igual, você definitivamente deve dar uma olhada na ng-repeat virtualizada: http://kamilkp.github.io/angular-vs-repeat/

Esta demonstração parece muito promissora (e suporta rolagem inercial)

bartekp
fonte
2
Desempenho de rolagem no celular não é aceitável (que eventos de rolagem não dispara no iOS móveis (somente acima de 8)
Johny
9

Regra No.1: Nunca deixe o usuário esperar por nada.

Isso significa que uma página em crescimento que precisa de 10 segundos aparece muito mais rápido do que esperar 3 segundos antes de uma tela em branco e obter tudo de uma vez.

Então, ao invés de fazer a página rápido, apenas deixe a página aparecer para ser rápido, mesmo que o resultado final é mais 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
    }
}

O código acima permite que a lista esteja crescendo linha por linha e é sempre mais lenta que a renderização de uma só vez. Mas para o usuário parece ser mais rápido.

Steffomio
fonte
como usar esta função na página html?
Antonis
9

A rolagem virtual é outra maneira de melhorar o desempenho da rolagem ao lidar com listas enormes e grandes conjuntos de dados.

Uma maneira de implementar isso é usar o Material Angular, md-virtual-repeat como é demonstrado nesta demonstração com 50.000 itens

Retirado diretamente da documentação da repetição virtual:

A repetição virtual é um substituto limitado para ng-repeat que renderiza apenas nós dom suficientes para encher o contêiner e reciclá-los à medida que o usuário rola.

Tofas Sarantis
fonte
2
Uau, acho que essa é a resposta mais interessante. Isso funciona com a versão mais antiga do angular? (por exemplo, versão 1.2)
Thariq Nugrohotomo 23/08
2
@ThariqNugrohotomo Observe que o uso de material angular requer o uso do Angular 1.3.x ou superior. Também obrigado pelo apoio, também estou impressionado com a repetição virtual e já o usamos em um aplicativo móvel que exibe uma lista realmente longa de resultados.
Sarantis Tofas
6

Outra versão @Steffomio

Em vez de adicionar cada item individualmente, podemos adicionar itens 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
fonte
Idéia interessante. Eu tentei isso em uma matriz de ~ 8000 elementos e, embora tenha tornado a página mais responsiva inicialmente, ela se tornou menos responsiva após cada pedaço.
Paul Brannan
Esse foi um grande problema no meu aplicativo depois de ter mais de 500 itens. Sugiro paginação ou carregamento infinito.
joalcego 7/04
0

Às vezes, o que aconteceu, você obtém os dados do servidor (ou back-end) em poucos ms (por exemplo, estou assumindo 100ms), mas leva mais tempo para ser exibido em nossa página da web (digamos que está levando 900ms para exibição).

Então, o que está acontecendo aqui são 800ms. É preciso apenas para renderizar a página da web.

O que fiz no meu aplicativo da web é que usei a paginação (ou você pode usar a rolagem infinita também) para exibir a lista de dados. Digamos que estou mostrando 50 dados / página.

Portanto, não carregarei renderizar todos os dados de uma só vez, apenas 50 dados que estou carregando inicialmente, o que leva apenas 50ms (suponho aqui).

portanto, o tempo total aqui diminuiu de 900 ms para 150 ms, uma vez que o usuário solicita a próxima página, exibe os próximos 50 dados e assim por diante.

Espero que isso ajude você a melhorar o desempenho. Muito bem sucedida

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

que carrega dados quando chega à parte inferior da página e remove metade dos dados carregados anteriormente e quando chega ao topo da div novamente, os dados anteriores (dependendo do número da página) serão carregados removendo a metade dos dados atuais So on DOM por vez, apenas dados limitados estão presentes, o que pode levar a um melhor desempenho, em vez de renderizar dados inteiros em 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));
            }
          }
        }
    }
  };
});

Demonstração com diretiva

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

Dependendo da altura da divisão, ele carrega os dados e, após a rolagem, novos dados serão anexados e os dados anteriores serão removidos.

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;

    }
}

Demonstração com grade da interface do usuário com demonstração de rolagem infinita

ankesh jain
fonte
Um link para uma solução é bem-vindo, mas garanta que sua resposta seja útil sem ela: adicione contexto ao link para que seus colegas usuários tenham uma idéia do que é e por que está lá, depois cite a parte mais relevante da página que você ' reencaminhando para o caso de a página de destino não estar disponível. Respostas que são pouco mais que um link podem ser excluídas .
você precisa saber é o seguinte
-2

para grandes conjuntos de dados e vários valores suspensos, é melhor usar ng-optionsdo que usar ng-repeat.

ng-repeaté lento porque faz um loop sobre todos os valores futuros, mas ng-optionssimplesmente é exibido na opção de seleção.

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

muito, muito mais rápido que

<option ng-repeat="state in States" value="{{state.StateCode}}">
    {{state.StateName }}
</option>
Ghebrehiywet
fonte
Você verificou o desempenho das opções ng? Estou tentando otimizar meu código e não ajudou. A velocidade é a mesma que ng-repeat. -1
Icet 4/17/17
só funciona para selecionar, ng-repeat é muito mais poderoso. No entanto, é verdade que ng-Options é muito mais rápido que ng-repeat. AngularJS docs menciona 2000 itens para diferenças: docs.angularjs.org/api/ng/directive/select
kaiser