Comment améliorer les performances de ngRepeat sur un énorme jeu de données (angular.js)?

165

J'ai un énorme ensemble de données de plusieurs milliers de lignes avec environ 10 champs chacun, environ 2 Mo de données. J'ai besoin de l'afficher dans le navigateur. L'approche la plus simple (récupérer des données, les insérer $scope, laisser ng-repeat=""faire son travail) fonctionne bien, mais elle gèle le navigateur pendant environ une demi-minute lorsqu'il commence à insérer des nœuds dans DOM. Comment dois-je aborder ce problème?

Une option consiste à ajouter des lignes de manière $scopeincrémentielle et d'attendre la ngRepeatfin de l'insertion d'un morceau dans le DOM avant de passer au suivant. Mais AFAIK ngRepeat ne rend pas compte quand il a fini de "répéter", donc ça va être moche.

Une autre option consiste à diviser les données sur le serveur en pages et à les récupérer en plusieurs requêtes, mais c'est encore plus laid.

J'ai parcouru la documentation Angular à la recherche de quelque chose comme ng-repeat="data in dataset" ng-repeat-steps="500", mais je n'ai rien trouvé. Je suis assez nouveau dans les méthodes angulaires, il est donc possible que je manque complètement le point. Quelles sont les meilleures pratiques à ce sujet?

n1313
la source
10
Voulez-vous vraiment afficher TOUTES les lignes? Qu'en est-il d'afficher uniquement le nombre de lignes que l'utilisateur peut voir. par exemple, vous pouvez utiliser limitTopour afficher seulement 20 éléments: <p ng-repeat="data in dataset | limitTo:20">{{data}}</p>Cela montre seulement 20 éléments. Ensuite, vous pouvez utiliser des pages et afficher les 10 éléments suivants ou quelque chose comme ça. :)
AndreM96
pour ce "rapport quand il a fini de" répéter "", vous pouvez utiliser une directive personnalisée en plus de ng-repeat. (voir ici la réponse sélectionnée) stackoverflow.com/questions/13471129/…
mayankcpdixit
renvoyez cette question, cela vous aidera sûrement. [entrez la description du lien ici] [1] [1]: stackoverflow.com/questions/25481021/…
Mahesh

Réponses:

159

Je suis d'accord avec @ AndreM96 que la meilleure approche consiste à n'afficher qu'un nombre limité de lignes, une UX plus rapide et meilleure, cela pourrait être fait avec une pagination ou avec un défilement infini.

Le défilement infini avec Angular est vraiment simple avec le filtre limitTo . Il vous suffit de définir la limite initiale et lorsque l'utilisateur demande plus de données (j'utilise un bouton pour plus de simplicité), vous incrémentez la 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;

Voici un JsBin .

Cette approche pourrait être un problème pour les téléphones car ils sont généralement à la traîne lors du défilement de beaucoup de données, donc dans ce cas, je pense qu'une pagination convient mieux.

Pour ce faire, vous aurez besoin du filtre limitTo et également d'un filtre personnalisé pour définir le point de départ des données affichées.

Voici un JSBin avec une pagination.

Bertrand
la source
Belle alternative !!! Vous connaissez n'importe quelle méthode à utiliser si je suis obligé de montrer tous les éléments. un signe de chargement ou un après une insertion dans DOM ou quelque chose?
mayankcpdixit
Voulez-vous dire afficher un "chargement ..." ou quelque chose pendant que les données sont extraites?
Bertrand
1
@Sumit limitTo sera appliqué à la portée ng-repeat, donc le résultat sera un nouveau tableau qui sera passé à ng-repeat, votre tableau de données reste le même et vous pouvez toujours rechercher tout le contenu.
Bertrand
12
si l'utilisateur appuie 10 fois sur load et que chaque pression ajoute 100 éléments supplémentaires, comment cela peut-il améliorer les performances?
hariszaman
5
@hariszaman Je suis d'accord. Cela n'améliore pas les performances. Cela retarde simplement les mauvaises performances. Le défilement infini vous posera des problèmes à moins que vous ne le virtualisiez (ce que fait ui-grid).
richard
41

L'approche la plus chaude - et sans doute la plus évolutive - pour surmonter ces défis avec de grands ensembles de données est incarnée par l'approche de la directive collectionRepeat d' Ionic et d'autres implémentations similaires. Un terme sophistiqué pour cela est `` culling d'occlusion '' , mais vous pouvez le résumer comme suit: ne limitez pas simplement le nombre d'éléments DOM rendus à un nombre paginé arbitraire (mais toujours élevé) comme 50, 100, 500 ... , limitez-vous à autant d'éléments que l'utilisateur peut voir .

Si vous faites quelque chose comme ce que l'on appelle communément le "défilement infini", vous réduisez quelque peu le nombre initial de DOM, mais il gonfle rapidement après quelques rafraîchissements, car tous ces nouveaux éléments sont simplement cloués en bas. Le défilement est une analyse, car le défilement est une question de nombre d'éléments. Il n'y a rien d'infini à ce sujet.

Tandis que l' collectionRepeatapproche consiste à n'utiliser que le nombre d'éléments pouvant tenir dans la fenêtre, puis à les recycler . Lorsqu'un élément tourne hors de la vue, il est détaché de l'arborescence de rendu, rempli de données pour un nouvel élément dans la liste, puis rattaché à l'arborescence de rendu à l'autre extrémité de la liste. C'est le moyen le plus rapide connu de l'homme pour obtenir de nouvelles informations dans et hors du DOM, en utilisant un ensemble limité d'éléments existants, plutôt que le cycle traditionnel de création / destruction ... création / destruction. En utilisant cette approche, vous pouvez vraiment implémenter un défilement infini .

Notez que vous n'avez pas besoin d'utiliser Ionic pour utiliser / hack / adapt collectionRepeat, ou tout autre outil similaire. C'est pourquoi ils l'appellent open-source. :-) (Cela dit, l'équipe Ionic fait des choses assez ingénieuses, dignes de votre attention.)


Il y a au moins un excellent exemple de quelque chose de très similaire dans React. Seulement au lieu de recycler les éléments avec un contenu mis à jour, vous choisissez simplement de ne pas rendre quoi que ce soit dans l'arborescence qui n'est pas visible. C'est extrêmement rapide sur 5000 éléments, bien que leur implémentation POC très simple permette un peu de scintillement ...


Aussi ... pour faire écho à certains des autres articles, l'utilisation track byest très utile, même avec des ensembles de données plus petits. Considérez cela comme obligatoire.

XML
la source
Super idée de l'équipe Ionic. Je me demande si cela vient de la façon dont les vues natives sont rendues?
Bradley Flood
Par exemple, UITableView dans iOS utilise la même approche pour rendre de grands ensembles de données. Je pense que c'est une approche courante utilisée dans de nombreuses vues natives.
Dmitry Kotenko le
36

Je recommande de voir ceci:

Optimisation d'AngularJS: 1200 ms à 35 ms

ils ont fait une nouvelle directive en optimisant ng-repeat en 4 parties:

Optimisation n ° 1: cache les éléments DOM

Optimisation n ° 2: observateurs agrégés

Optimisation n ° 3: différer la création des éléments

Optimisation n ° 4: contourner les observateurs pour les éléments cachés

le projet est ici sur github:

Usage:

1- Incluez ces fichiers dans votre application monopage:

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

2- Ajouter une dépendance de module:

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

3- remplacer ng-repeat

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

Prendre plaisir!

pixparker
la source
4
Je pense que ce scalyr.js inclut déjà les autres fichiers. Parce que c'est le résultat du script de construction.
dnocode le
J'ai essayé d'utiliser Scalyr mais le filtre ne fonctionne pas. <tr sly-repeat = "option dans main.customers | filter: search_input | limitTo: 20">
aldesabido
Ceci est extrêmement utile. Je l'utilise sur une application AngularJS 1.6 où le client veut voir beaucoup de cellules de données (normalement, je conçois des formulaires avec une pagination / des éléments de données réduits, mais le client doit comparer beaucoup de données à la fois). Jusqu'à présent, la grille de cellules est passée d'inutilisable à parfaitement correcte à cause de cette bibliothèque. Mais cette bibliothèque a été écrite il y a longtemps dans AngularJS 1.2 jours, je vais donc tester attentivement les problèmes.
flyer
D'après ce que je peux dire pour le moment, le fichier gatedScope.js (323 lignes) est le seul qui doit être vérifié pour être exécutable sur les versions plus récentes d'AngularJS. Cette pull request est remarquable: github.com/karser/angular/commit/… . Il met à jour la signature rootScope. $ New.
flyer
inclus les quatre fichiers js et utilisé, sly-repeatmais rien ne m'a aidé, les résultats sont toujours lents et le navigateur retarde également les violations [Violation] 'setTimeout' handler took 54ms,[Violation] 'scroll' handler took 1298ms
Gaurav Aggarwal
15

En plus de tous les conseils ci-dessus, comme la piste par et les boucles plus petites, celui-ci m'a également beaucoup aidé

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

ce morceau de code afficherait le nom une fois qu'il a été chargé, et arrêterait de le regarder après cela. De même, pour ng-repeats, il pourrait être utilisé comme

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

cependant cela ne fonctionne que pour AngularJS version 1.3 et supérieure. De http://www.befundoo.com/blog/optimizing-ng-repeat-in-angularjs/

Shilan
la source
Avez-vous besoin ::de la répétition ainsi que de l'expression? La documentation dit le contraire, mais je ne suis pas sûr de savoir si cela fonctionne. docs.angularjs.org/guide/expression
Crhistian Ramirez
12

Vous pouvez utiliser "track by" pour augmenter les performances:

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

Plus rapide que:

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

réf: https://www.airpair.com/angularjs/posts/angularjs-performance-large-applications

user1920302
la source
1
Cela n'aide pas vraiment les performances. Voir jsperf.com/ng-repeat-vs-ng-repeat-with-trace-by-id
ilter
avec track by vous ne suivez pas l'élément du tableau depuis le début chaque fois que vous obtenez de nouvelles données. en conséquence, cela améliore les performances.
user1920302
2
Ceci n'est utile que lorsque les données du ng-repeat changent. Pour le chargement initial, cela peut ne pas créer une amélioration des performances.
Sumesh Kuttan
11

Si toutes vos lignes ont la même hauteur, vous devez absolument jeter un œil à la virtualisation ng-repeat: http://kamilkp.github.io/angular-vs-repeat/

Cette démo semble très prometteuse (et elle prend en charge le défilement inertiel)

bartekp
la source
2
Les performances de défilement sur mobile ne sont pas acceptables (les événements de défilement ne se déclenchent pas sur iOS mobile (uniquement à partir de 8)
Johny
9

Règle n ° 1: ne laissez jamais l'utilisateur attendre quoi que ce soit.

Cela à l'esprit qu'une page en pleine croissance qui a besoin de 10 secondes apparaît bien plus rapidement que d'attendre 3 secondes devant un écran vide et d'obtenir tout en même temps.

Donc, au lieu de rendre la page rapide, laissez simplement la page paraître rapide, même si le résultat final est plus lent:

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

Le code ci-dessus laisse apparaître la liste pour croître ligne par ligne, et est toujours plus lent que le rendu en une seule fois. Mais pour l'utilisateur, cela semble être plus rapide.

Steffomio
la source
comment utiliser cette fonction dans la page html?
Antonis
9

Défilement virtuel est un autre moyen d'améliorer les performances de défilement lors du traitement de listes volumineuses et d'un ensemble de données volumineux.

Une façon de l'implémenter consiste à utiliser le matériau angulaire md-virtual-repeat comme il est démontré dans cette démo avec 50000 éléments

Tiré directement de la documentation de la répétition virtuelle:

La répétition virtuelle est un substitut limité à ng-repeat qui ne rend que suffisamment de nœuds dom pour remplir le conteneur et les recycler lorsque l'utilisateur fait défiler.

Sarantis Tofas
la source
2
Wow, je pense que c'est la réponse la plus intéressante. Cela fonctionnera-t-il avec une ancienne version d'angular? (par exemple ver 1.2)
Thariq Nugrohotomo
2
@ThariqNugrohotomo Veuillez noter que l'utilisation du matériau angulaire nécessite l'utilisation de Angular 1.3.x ou supérieur. Merci également pour le soutien, je suis également vraiment étonné par la répétition virtuelle et nous l'utilisons déjà sur une application mobile qui affiche une très longue liste de résultats.
Sarantis Tofas
6

Une autre version @Steffomio

Au lieu d'ajouter chaque élément individuellement, nous pouvons ajouter des éléments par morceaux.

// 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
la source
Idée intéressante. J'ai essayé cela sur un tableau d'environ 8000 éléments, et bien que cela ait rendu la page plus réactive au départ, elle est devenue moins réactive après chaque morceau.
Paul Brannan
C'était un énorme problème sur mon application après avoir eu plus de 500 articles. Je suggère plutôt la pagination ou le chargement infini.
joalcego
0

Parfois, ce qui s'est passé, vous obtenez les données du serveur (ou du back-end) en quelques ms (par exemple, je suppose que cela prend 100 ms ) mais cela prend plus de temps à s'afficher sur notre page Web (disons que cela prend 900 ms pour afficher).

Donc, ce qui se passe ici est de 800 ms. Il suffit de rendre la page Web.

Ce que j'ai fait dans mon application Web, c'est que j'ai utilisé la pagination (ou vous pouvez également utiliser le défilement infini ) pour afficher la liste des données. Disons que je montre 50 données / page.

Je ne chargerai donc pas le rendu de toutes les données à la fois, seulement 50 données que je charge initialement, ce qui ne prend que 50 ms (je suppose ici).

donc le temps total ici a diminué de 900 ms à 150 ms, une fois que l'utilisateur demande la page suivante, puis affiche les 50 données suivantes et ainsi de suite.

J'espère que cela vous aidera à améliorer les performances. Bonne chance

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

qui charge les données quand il atteint le bas de la page et supprime la moitié des données précédemment chargées et quand il atteint le haut de la div à nouveau les données précédentes (en fonction du numéro de page) seront chargées en supprimant la moitié des données actuelles Donc sur DOM à la fois, seules des données limitées sont présentes, ce qui peut conduire à de meilleures performances au lieu de restituer des données entières en charge.

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

CODE angulaire:

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

Démo avec directive

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

En fonction de la hauteur de la division, il charge les données et lors du défilement, de nouvelles données seront ajoutées et les données précédentes seront supprimées.

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

Code angulaire:

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;

    }
}

Démo avec grille d'interface utilisateur avec démo à défilement infini

ankesh jain
la source
Un lien vers une solution est le bienvenu, mais veuillez vous assurer que votre réponse est utile sans elle: ajoutez du contexte autour du lien afin que vos collègues utilisateurs aient une idée de ce que c'est et pourquoi il est là, puis citez la partie la plus pertinente de la page que vous '' relier au cas où la page cible n'est pas disponible. Les réponses qui ne sont guère plus qu'un lien peuvent être supprimées .
Sᴀᴍ le
-2

pour un ensemble de données volumineux et une liste déroulante de valeurs multiples, il est préférable d'utiliser ng-optionsplutôt que ng-repeat.

ng-repeatest lent car il boucle sur toutes les valeurs à venir mais ng-optionsaffiche simplement l'option de sélection.

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

beaucoup plus vite que

<option ng-repeat="state in States" value="{{state.StateCode}}">
    {{state.StateName }}
</option>
Ghebrehiywet
la source
Avez-vous vérifié les performances des ng-options? J'essaie d'optimiser mon code et cela n'a pas aidé. La vitesse est la même que ng-repeat. -1
Icet
ne fonctionne que pour select, ng-repeat est bien plus puissant. Néanmoins, il est vrai que ng-Options est bien plus rapide que ng-repeat. AngularJs docs mentionne 2000 éléments pour les différences: docs.angularjs.org/api/ng/directive/select
kaiser