Come migliorare le prestazioni di ngRepeat su un enorme set di dati (angular.js)?


165

Ho un enorme set di dati di diverse migliaia di righe con circa 10 campi ciascuna, circa 2 MB di dati. Devo visualizzarlo nel browser. L'approccio più semplice (recuperare i dati, inserirli $scope, ng-repeat=""fare il proprio lavoro) funziona bene, ma congela il browser per circa mezzo minuto quando inizia a inserire nodi in DOM. Come dovrei affrontare questo problema?

Un'opzione è aggiungere le righe in modo $scopeincrementale e attendere il ngRepeatcompletamento dell'inserimento di un blocco nel DOM prima di passare a quello successivo. Ma AFAIK ngRepeat non riporta indietro quando finisce di "ripetere", quindi sarà brutto.

Un'altra opzione è quella di dividere i dati sul server in pagine e recuperarli in più richieste, ma è ancora più brutto.

Ho cercato la documentazione angolare alla ricerca di qualcosa del genere ng-repeat="data in dataset" ng-repeat-steps="500", ma non ho trovato nulla. Sono abbastanza nuovo nei modi angolari, quindi è possibile che mi manchi completamente il punto. Quali sono le migliori pratiche a questo?


10
Vuoi davvero visualizzare TUTTE le righe? Che ne dici di visualizzare solo tutte le righe che l'utente può vedere. ad esempio, è possibile utilizzare limitToper visualizzare solo 20 voci: <p ng-repeat="data in dataset | limitTo:20">{{data}}</p>mostra solo 20 voci. Quindi potresti usare le pagine e mostrare i prossimi 10 elementi o qualcosa del genere. :)
AndreM96,

per quella "segnalazione quando finisce 'ripetendo'" potresti usare una direttiva personalizzata oltre a ng-repeat. (vedi qui la risposta selezionata) stackoverflow.com/questions/13471129/...
mayankcpdixit

fai questa domanda sicuramente ti aiuterà. [inserisci la descrizione del link qui] [1] [1]: stackoverflow.com/questions/25481021/…
Mahesh,

Risposte:


159

Sono d'accordo con @ AndreM96 che l'approccio migliore è quello di visualizzare solo una quantità limitata di righe, UX più veloce e migliore, questo potrebbe essere fatto con una paginazione o con uno scorrimento infinito.

Lo scorrimento infinito con Angular è davvero semplice con limitTo filter. Devi solo impostare il limite iniziale e quando l'utente richiede più dati (sto usando un pulsante per semplicità), aumenti il ​​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;

Ecco un JsBin .

Questo approccio potrebbe essere un problema per i telefoni perché di solito sono in ritardo durante lo scorrimento di molti dati, quindi in questo caso penso che una paginazione si adatti meglio.

Per farlo avrai bisogno del filtro limitTo e anche di un filtro personalizzato per definire il punto iniziale dei dati visualizzati.

Ecco un JSBin con una paginazione.


Bella alternativa !!! Conosci un metodo da usare se sono tenuto a mostrare tutti gli elementi. qualche segno di caricamento o uno dopo un inserimento in DOM o qualcosa del genere?
Mayankcpdixit,

Intendi visualizzare un "caricamento in corso ..." o qualcosa del genere durante il recupero dei dati?
Bertrand,

1
@Sumit limitTo verrà applicato all'ambito di ng-repeat, quindi il risultato sarà un nuovo array che verrà passato a ng-repeat, il tuo array di dati sarà sempre lo stesso e potrai comunque cercare tutto il contenuto.
Bertrand,

12
se l'utente preme loadmore 10 volte e ogni stampa aggiunge altri 100 elementi, come può migliorare le prestazioni?
Hariszaman,

5
@hariszaman Sono d'accordo. Questo non migliora le prestazioni. Ritarda solo le scarse prestazioni. Lo scorrimento infinito ti metterà nei guai a meno che tu non lo stia virtualizzando (cosa che fa ui-grid).
Richard

41

L'approccio più caldo - e probabilmente più scalabile - per superare queste sfide con grandi set di dati è rappresentato dall'approccio della direttiva Ionic collectionRepeat e da altre implementazioni come questa. Un termine fantasioso per questo è "occlusione culling" , ma puoi riassumerlo come: non limitare il conteggio degli elementi DOM renderizzati a un numero impaginato arbitrario (ma comunque elevato) come 50, 100, 500 ... invece , limita solo a tutti gli elementi che l'utente può vedere .

Se fai qualcosa come quello che è comunemente noto come "scrolling infinito", stai riducendo un po 'il conteggio iniziale del DOM, ma si gonfia rapidamente dopo un paio di aggiornamenti, perché tutti quei nuovi elementi sono appena appiccicati sul fondo. Lo scorrimento arriva a una scansione, perché lo scorrimento riguarda il conteggio degli elementi. Non c'è nulla di infinito.

Considerando che, l' collectionRepeatapproccio consiste nell'utilizzare solo tutti gli elementi che rientrano nel viewport e quindi riciclarli . Quando un elemento ruota fuori dalla vista, viene staccato dall'albero di rendering, riempito con i dati per un nuovo elemento nell'elenco, quindi ricollegato all'albero di rendering all'altra estremità dell'elenco. Questo è il modo più veloce conosciuto dall'uomo per ottenere nuove informazioni dentro e fuori il DOM, facendo uso di un insieme limitato di elementi esistenti, piuttosto che il tradizionale ciclo di creare / distruggere ... creare / distruggere. Usando questo approccio, puoi veramente implementare uno scroll infinito .

Nota che non devi usare Ionic per usare / hack / adapt collectionRepeat, o qualsiasi altro strumento simile. Ecco perché lo chiamano open-source. :-) (Detto questo, il team ionico sta facendo cose piuttosto ingegnose, degne della tua attenzione.)


C'è almeno un eccellente esempio di fare qualcosa di molto simile in React. Solo invece di riciclare gli elementi con contenuto aggiornato, stai semplicemente scegliendo di non rendere nulla nella struttura che non è visibile. È veloce su 5000 articoli, anche se la loro semplice implementazione POC consente un po 'di sfarfallio ...


Inoltre ... per fare eco ad alcuni degli altri post, l'utilizzo track byè estremamente utile, anche con set di dati più piccoli. Consideralo obbligatorio.


Fantastica idea del team ionico. Mi chiedo se ciò provenga da come vengono visualizzate le visualizzazioni native?
Bradley Flood,

Ad esempio, UITableView in iOS utilizza lo stesso approccio per il rendering di set di dati di grandi dimensioni. Penso che questo sia un approccio comune utilizzato in molte visualizzazioni native.
Dmitry Kotenko,

36

Consiglio di vedere questo:

Ottimizzazione di AngularJS: da 1200ms a 35ms

hanno fatto una nuova direttiva ottimizzando ng-repeat in 4 parti:

Ottimizzazione n. 1: elementi DOM della cache

Ottimizzazione n. 2: osservatori aggregati

Ottimizzazione n. 3: differire la creazione dell'elemento

Ottimizzazione # 4: ignora gli osservatori per gli elementi nascosti

il progetto è qui su github:

Uso:

1- includi questi file nella tua app a pagina singola:

  • Core.js
  • scalyr.js
  • slyEvaluate.js
  • slyRepeat.js

2- aggiungi dipendenza modulo:

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

3- sostituire ng-repeat

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

Godere!


4
Penso che questo scalyr.js includa già gli altri file. Perché è il risultato dello script di compilazione.
dnocode

Ho provato ad usare Scalyr ma il filtro non funziona. <tr sly-repeat = "opzione in main.customers | filtro: search_input | limitTo: 20">
aldesabido

Questo è estremamente utile. Lo sto usando su un'app AngularJS 1.6 in cui il cliente vuole vedere molte celle di dati (normalmente disegno moduli con elementi di paging / dati ridotti ma il cliente deve confrontare molti dati contemporaneamente). Finora la griglia di celle è passata da inutilizzabile a perfettamente bene a causa di questa libreria. Ma questa lib è stata scritta molto tempo fa negli AngularJS 1.2, quindi testerò attentamente alla ricerca di problemi.
volantino

Da quello che posso dire in questo momento il file gatedScope.js (323 righe) è l'unico che deve essere verificato per essere eseguibile su più versioni correnti di AngularJS. Questa richiesta pull è notevole: github.com/karser/angular/commit/… . Aggiorna la firma rootScope. $ New.
volantino

inclusi tutti e quattro i file js e utilizzati sly-repeatma nulla mi ha aiutato i risultati sono ancora lenti e anche i ritardi del browser hanno subito violazioni [Violation] 'setTimeout' handler took 54ms,[Violation] 'scroll' handler took 1298ms
Gaurav Aggarwal

15

Oltre a tutti i suggerimenti di cui sopra come track by e loop più piccoli, anche questo mi ha aiutato molto

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

questo pezzo di codice stampa il nome una volta che è stato caricato e smette di guardarlo dopo. Allo stesso modo, per ng-repeats, potrebbe essere usato come

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

tuttavia funziona solo per AngularJS versione 1.3 e successive. Da http://www.befundoo.com/blog/optimizing-ng-repeat-in-angularjs/


Hai bisogno della ::ripetizione e dell'espressione? I documenti dicono diversamente, ma non sono sicuro del modo per testare che funzioni. docs.angularjs.org/guide/expression
Crhistian Ramirez

12

È possibile utilizzare "track by" per aumentare le prestazioni:

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

Più veloce di:

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

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


1
Questo non aiuta davvero per le prestazioni. Vedere jsperf.com/ng-repeat-vs-ng-repeat-with-trace-by-id
ilter

con track by non traccia l'elemento array dall'inizio ogni volta che ottieni nuovi dati. di conseguenza ciò migliora le prestazioni.
user1920302

2
Ciò è utile solo quando i dati in ng-repeat cambiano. Per il caricamento iniziale, potrebbe non creare un miglioramento delle prestazioni.
Sumesh Kuttan,

11

Se tutte le righe hanno la stessa altezza, dovresti assolutamente dare un'occhiata alla virtualizzazione di ng-repeat: http://kamilkp.github.io/angular-vs-repeat/

Questa demo sembra molto promettente (e supporta lo scorrimento inerziale)


2
Le prestazioni di scorrimento su dispositivi mobili non sono accettabili (gli eventi di scorrimento non si attivano su dispositivi mobili iOS (solo fino alle 8)
Johny,

9

Regola n. 1: non lasciare mai che l'utente aspetti nulla.

Che in mente una pagina in crescita che richiede 10 secondi appare molto più veloce di aspettare 3 secondi prima di uno schermo vuoto e ottenere tutto in una volta.

Quindi, invece di rendere la pagina veloce, lascia che la pagina appaia veloce, anche se il risultato finale è più 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
    }
}

Il codice sopra riportato fa apparire l'elenco in crescita riga per riga ed è sempre più lento del rendering tutto in una volta. Ma per l'utente sembra essere più veloce.


come usare questa funzione nella pagina html?
Antonis,

9

Lo scorrimento virtuale è un altro modo per migliorare le prestazioni di scorrimento quando si ha a che fare con elenchi enormi e set di dati di grandi dimensioni.

Un modo per implementarlo è usare il materiale angolare md-virtual-repeat come è dimostrato in questa demo con 50.000 articoli

Tratto direttamente dalla documentazione di ripetizione virtuale:

La ripetizione virtuale è un sostituto limitato di ng-repeat che esegue il rendering di un numero sufficiente di dom dom per riempire il contenitore e riciclarli mentre l'utente scorre.


2
Wow, penso che questa sia la risposta più interessante. Funzionerà con la versione precedente di angolare? (es. ver 1.2)
Thariq Nugrohotomo,

2
@ThariqNugrohotomo Si noti che l'utilizzo del materiale angolare richiede l'uso di Angular 1.3.xo superiore. Inoltre, grazie per il supporto, sono davvero sorpreso dalla ripetizione virtuale e lo stiamo già utilizzando su un'app mobile che mostra un elenco di risultati davvero lungo.
Sarantis Tofas,

6

Un'altra versione @Steffomio

Invece di aggiungere ogni articolo singolarmente, possiamo aggiungere elementi per blocchi.

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

Idea interessante. Ho provato questo su un array di ~ 8000 elementi e, sebbene inizialmente abbia reso la pagina più reattiva, è diventata meno reattiva dopo ogni blocco.
Paul Brannan,

Questo è stato un grosso problema sulla mia app dopo aver avuto più di 500 articoli. Suggerisco invece l'impaginazione o il caricamento infinito.
Joalcego,

0

A volte ciò che è accaduto, si ottengono i dati dal server (o dal back-end) in pochi ms (ad esempio, suppongo che siano 100ms) ma ci vuole più tempo per essere visualizzati nella nostra pagina Web (supponiamo che ci vogliano 900ms per Schermo).

Quindi, quello che sta succedendo qui è 800ms Ci vuole solo per rendere la pagina web.

Quello che ho fatto nella mia applicazione web è che ho usato l' impaginazione (o puoi usare anche lo scorrimento infinito ) per visualizzare un elenco di dati. Diciamo che sto mostrando 50 dati / pagina.

Quindi non caricherò renderizzare tutti i dati in una volta, solo 50 dati che sto caricando inizialmente che richiede solo 50ms (suppongo qui).

quindi il tempo totale qui è diminuito da 900ms a 150ms, una volta che l'utente ha richiesto la pagina successiva, visualizza i successivi 50 dati e così via.

Spero che questo ti aiuti a migliorare le prestazioni. Ti auguro il meglio


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

che carica i dati quando raggiunge la fine della pagina e rimuove metà dei dati caricati in precedenza e quando raggiunge nuovamente la parte superiore del div i dati precedenti (a seconda del numero di pagina) verranno caricati rimuovendo metà dei dati attuali. Quindi su DOM alla volta sono presenti solo dati limitati che possono portare a prestazioni migliori invece di eseguire il rendering di dati interi al carico.

CODICE 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="angular.js@1.3.x" 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>

CODICE angolare:

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 direttiva

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

A seconda dell'altezza della divisione carica i dati e al momento dello scorrimento verranno aggiunti nuovi dati e i dati precedenti verranno rimossi.

Codice 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="angular.js@1.3.x" 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>

Codice angolare:

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;

    }
}

Demo con griglia dell'interfaccia utente con demo a scorrimento infinito


Un link a una soluzione è il benvenuto, ma assicurati che la tua risposta sia utile senza di essa: aggiungi un contesto attorno al link in modo che i tuoi colleghi utenti abbiano qualche idea di cosa sia e perché sia ​​lì, quindi cita la parte più pertinente della pagina che ' re collegamento a nel caso in cui la pagina di destinazione non sia disponibile. Le risposte che sono poco più di un collegamento possono essere eliminate .
Sᴀᴍ Onᴇᴌᴀ,

-2

per set di dati di grandi dimensioni e menu a discesa a più valori è preferibile utilizzare ng-optionsanziché ng-repeat.

ng-repeatè lento perché scorre su tutti i valori in arrivo ma viene ng-optionssemplicemente visualizzato sull'opzione di selezione.

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

molto più veloce di

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

Hai controllato le prestazioni di ng-options? Sto cercando di ottimizzare il mio codice e non ha aiutato. La velocità è la stessa di ng-repeat. -1
Icet

funziona solo con select, ng-repeat è molto più potente. Tuttavia è vero che ng-Options è molto più veloce di ng-repeat. I documenti di AngularJs menzionano 2000 articoli per differenze: docs.angularjs.org/api/ng/directive/select
kaiser
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.