Jak poprawić wydajność ngRepeat na ogromnym zbiorze danych (angular.js)?

165

Mam ogromny zbiór danych obejmujący kilka tysięcy wierszy z około 10 polami każdy, około 2 MB danych. Muszę to wyświetlić w przeglądarce. Najprostsze podejście (pobieranie danych, umieszczanie ich $scope, ng-repeat=""wykonywanie swojej pracy) działa dobrze, ale zawiesza przeglądarkę na około pół minuty, gdy zaczyna wstawiać węzły do ​​DOM. Jak mam podejść do tego problemu?

Jedną z opcji jest dodawanie wierszy $scopeprzyrostowo i czekanie na ngRepeatzakończenie wstawiania jednego fragmentu do DOM przed przejściem do następnego. Ale AFAIK ngRepeat nie zgłasza się po zakończeniu „powtarzania”, więc będzie brzydki.

Inną opcją jest podzielenie danych na serwerze na strony i pobranie ich w wielu żądaniach, ale to jeszcze brzydsze.

Przejrzałem dokumentację Angulara w poszukiwaniu czegoś podobnego ng-repeat="data in dataset" ng-repeat-steps="500", ale nic nie znalazłem. Jestem dość nowy w sposobach Angulara, więc możliwe, że całkowicie mija się z celem. Jakie są najlepsze praktyki w tym zakresie?

n1313
źródło
10
Czy na pewno chcesz wyświetlić WSZYSTKIE wiersze? A co z wyświetlaniem tylko tylu wierszy, które użytkownik widzi. np. możesz użyć limitTodo wyświetlenia tylko 20 elementów: <p ng-repeat="data in dataset | limitTo:20">{{data}}</p>Pokazuje tylko 20 elementów. Następnie możesz użyć stron i pokazać następne 10 elementów lub coś w tym rodzaju. :)
AndreM96
dla tej rzeczy „zgłoś raport po zakończeniu 'powtarzania'”, możesz użyć dyrektywy niestandardowej oprócz ng-repeat. (zobacz tutaj wybraną odpowiedź) stackoverflow.com/questions/13471129/…
mayankcpdixit
Skieruj to pytanie, na pewno ci pomoże. [wprowadź opis linku tutaj] [1] [1]: stackoverflow.com/questions/25481021/…
Mahesh

Odpowiedzi:

159

Zgadzam się z @ AndreM96, że najlepszym podejściem jest wyświetlanie tylko ograniczonej liczby wierszy, szybszego i lepszego UX, można to zrobić za pomocą paginacji lub nieskończonego przewijania.

Nieskończone przewijanie z Angular jest naprawdę proste dzięki filtrowi limitTo . Musisz tylko ustawić początkowy limit, a gdy użytkownik prosi o więcej danych (dla uproszczenia używam przycisku), zwiększasz limit.

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

Oto plik JsBin .

Takie podejście może być problemem w przypadku telefonów, ponieważ zwykle opóźniają się one podczas przewijania dużej ilości danych, więc w tym przypadku myślę, że paginacja pasuje lepiej.

Aby to zrobić, będziesz potrzebować filtra limitTo, a także niestandardowego filtru, aby zdefiniować punkt początkowy wyświetlanych danych.

Oto JSBin z paginacją.

Bertrand
źródło
Niezła alternatywa !!! Znasz każdą metodę, której możesz użyć, jeśli mam obowiązek pokazać wszystkie elementy. jakikolwiek znak ładowania czy jeden po jednym wstawieniu do DOM czy coś?
mayankcpdixit
Czy masz na myśli wyświetlanie komunikatu „ładowanie ...” czy coś w tym samym czasie podczas pobierania danych?
Bertrand,
1
@Sumit limitTo zostanie zastosowane do zakresu ng-repeat, więc wynikiem będzie nowa tablica, która zostanie przekazana do ng-repeat, twoja tablica danych nadal będzie taka sama i nadal możesz przeszukiwać całą zawartość.
Bertrand
12
Jeśli użytkownik naciśnie więcej 10 razy, a każde naciśnięcie dodaje 100 kolejnych pozycji, w jaki sposób może to poprawić wydajność?
hariszaman
5
@hariszaman Zgadzam się. Nie poprawia to wydajności. Po prostu opóźnia słabą wydajność. Nieskończony przewijanie wpędzi cię w kłopoty, chyba że go wirtualizujesz (co robi ui-grid).
Richard
41

Najgorętsze - i prawdopodobnie najbardziej skalowalne - podejście do pokonywania tych wyzwań w przypadku dużych zbiorów danych jest zawarte w dyrektywie Ionic CollectionRepeat i innych podobnych implementacjach. Wymyślnym terminem na to jest `` eliminacja okluzji '' , ale można to podsumować następująco: nie ograniczaj liczby renderowanych elementów DOM do dowolnej (ale wciąż wysokiej) liczby z paginami, takiej jak 50, 100, 500 ... zamiast tego , ogranicz tylko do tylu elementów, ile użytkownik widzi .

Jeśli zrobisz coś podobnego do tego, co jest powszechnie znane jako „nieskończone przewijanie”, nieco zmniejszasz początkową liczbę DOM, ale po kilku odświeżeniach szybko się powiększa, ponieważ wszystkie te nowe elementy są po prostu przyczepione na dole. Przewijanie dochodzi do indeksowania, ponieważ przewijanie polega na liczbie elementów. Nie ma w tym nic nieskończonego.

Natomiast collectionRepeatpodejście polega na użyciu tylko tylu elementów, ile zmieści się w rzutni, a następnie ich przetworzeniu . Gdy jeden element obraca się poza widok, jest odłączany od drzewa renderowania, uzupełniany danymi dla nowego elementu na liście, a następnie ponownie dołączany do drzewa renderowania na drugim końcu listy. Jest to najszybszy znany człowiekowi sposób uzyskiwania nowych informacji do i z DOM, wykorzystujący ograniczony zestaw istniejących elementów, zamiast tradycyjnego cyklu tworzenia / niszczenia ... tworzenia / niszczenia. Korzystając z tego podejścia, możesz naprawdę zaimplementować nieskończone przewijanie.

Zwróć uwagę, że nie musisz używać Ionic, aby używać / hack / adapt collectionRepeat, ani żadnego innego podobnego narzędzia. Dlatego nazywają to open-source. :-) (To powiedziawszy, zespół Ionic robi całkiem genialne rzeczy, warte twojej uwagi.)


Jest przynajmniej jeden doskonały przykład robienia czegoś bardzo podobnego w Reakcie. Tylko zamiast przetwarzać elementy ze zaktualizowaną zawartością, po prostu decydujesz się nie renderować niczego w drzewie, czego nie ma w widoku. Błyskawicznie działa na 5000 pozycji, chociaż ich bardzo prosta implementacja POC pozwala na odrobinę migotania ...


Ponadto ... aby powtórzyć niektóre inne posty, użycie track byjest bardzo pomocne, nawet w przypadku mniejszych zbiorów danych. Uważaj to za obowiązkowe.

XML
źródło
Świetny pomysł zespołu Ionic. Zastanawiam się, czy to wynikało z renderowania widoków natywnych?
Bradley Flood
Na przykład UITableView w systemie iOS używają tego samego podejścia do renderowania dużych zestawów danych. Myślę, że jest to powszechne podejście stosowane w wielu rodzimych widokach.
Dmitry Kotenko
36

Polecam to zobaczyć:

Optymalizacja AngularJS: 1200 ms do 35 ms

stworzyli nową dyrektywę, optymalizując powtarzanie ng w 4 częściach:

Optymalizacja nr 1: buforuj elementy DOM

Optymalizacja # 2: Agregacja obserwatorów

Optymalizacja # 3: Odłóż tworzenie elementów

Optymalizacja # 4: Pomiń obserwatorów w poszukiwaniu ukrytych elementów

projekt jest tutaj na github:

Stosowanie:

1- Uwzględnij te pliki w swojej aplikacji jednostronicowej:

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

2- Dodaj zależność od modułu:

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

3- wymień ng-powtórz

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

Cieszyć się!

pixparker
źródło
4
Myślę, że ten plik scalyr.js zawiera już inne pliki. Ponieważ jest wynikiem skryptu kompilacji.
dnocode
Próbowałem użyć Scalyr, ale filtr nie działa. <tr sly-repeat = "opcja w main.customers | filter: search_input | limitTo: 20">
aldesabido
Jest to niezwykle pomocne. Używam go w aplikacji AngularJS 1.6, w której klient chce zobaczyć wiele komórek danych (zwykle projektuję formularze ze stronicowaniem / zredukowanymi elementami danych, ale klient musi porównać wiele danych naraz). Jak dotąd, dzięki tej bibliotece, siatka komórek zmieniła się z bezużytecznej na całkowicie dobrą. Ale ta biblioteka została napisana w AngularJS 1.2 dni, więc będę dokładnie testować, szukając problemów.
ulotka
Z tego, co mogę powiedzieć w tej chwili, plik gatedScope.js (323 linie) jest jedynym, który musi zostać zweryfikowany, aby działał na bardziej aktualnych wersjach AngularJS. To żądanie ściągnięcia jest godne uwagi: github.com/karser/angular/commit/… . Aktualizuje sygnaturę rootScope. $ New.
ulotka
zawiera wszystkie cztery pliki js i jest używany, sly-repeatale nic mi nie pomogło, wyniki są nadal powolne, a przeglądarka opóźnia się również z naruszeniami [Violation] 'setTimeout' handler took 54ms,[Violation] 'scroll' handler took 1298ms
Gaurav Aggarwal,
15

Oprócz wszystkich powyższych wskazówek, takich jak track by i mniejsze pętle, ta również bardzo mi pomogła

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

ten fragment kodu wypisze nazwę po załadowaniu i przestanie go oglądać. Podobnie, dla powtórzeń ng, można go użyć jako

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

jednak działa tylko z AngularJS w wersji 1.3 i nowszych. Z http://www.befundoo.com/blog/optimizing-ng-repeat-in-angularjs/

Shilan
źródło
Czy potrzebujesz zarówno ::powtórzenia, jak i wyrażenia? Doktorzy mówią inaczej, ale nie jestem pewien sposobu sprawdzenia, czy to działa. docs.angularjs.org/guide/expression
Crhistian Ramirez,
12

Aby zwiększyć wydajność, możesz użyć funkcji „śledź według”:

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

Szybszy niż:

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

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

user1920302
źródło
1
To naprawdę nie pomaga w wydajności. Zobacz jsperf.com/ng-repeat-vs-ng-repeat-with-trace-by-id
ILTER
z track by nie śledzisz elementu tablicy od początku za każdym razem, gdy otrzymujesz nowe dane. w rezultacie poprawia to wydajność.
user1920302
2
Jest to przydatne tylko wtedy, gdy zmieniają się dane w powtarzaniu ng. Przy początkowym ładowaniu może nie powodować poprawy wydajności.
Sumesh Kuttan
11

Jeśli wszystkie twoje wiersze mają równą wysokość, zdecydowanie powinieneś przyjrzeć się wirtualizacji ng-repeat: http://kamilkp.github.io/angular-vs-repeat/

To demo wygląda bardzo obiecująco (i obsługuje przewijanie inercyjne)

bartekp
źródło
2
Wydajność przewijania na urządzeniach mobilnych jest niedopuszczalna (zdarzenia przewijania nie są uruchamiane na urządzeniach mobilnych z systemem iOS (tylko od 8)
Johny
9

Zasada nr 1: Nigdy nie pozwól użytkownikowi czekać na nic.

Mając to na uwadze, strona rozwijająca się, która potrzebuje 10 sekund, pojawia się znacznie szybciej niż czekanie 3 sekundy na pusty ekran i otrzymanie wszystkiego naraz.

Zamiast więc przyspieszać stronę, po prostu pozwól, aby strona wyglądała na szybką, nawet jeśli wynik końcowy jest wolniejszy:

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

Powyższy kod sprawia, że ​​lista rośnie wiersz po wierszu i jest zawsze wolniejsza niż renderowanie wszystkich naraz. Ale dla użytkownika wydaje się to być szybsze.

Steffomio
źródło
jak korzystać z tej funkcji na stronie html?
Antonis
9

Wirtualne przewijanie to kolejny sposób na poprawę wydajności przewijania w przypadku ogromnych list i dużych zbiorów danych.

Jednym ze sposobów realizacji tego jest użycie Angular Material, md-virtual-repeat jak pokazano w tym Demo z 50000 elementami

Zaczerpnięte prosto z dokumentacji wirtualnej powtórki:

Wirtualne powtórzenie jest ograniczonym substytutem ng-powtórzeń, które renderuje tylko wystarczającą liczbę węzłów dom, aby wypełnić kontener i ponownie je przetworzyć, gdy użytkownik przewija.

Sarantis Tofas
źródło
2
Wow, myślę, że to najciekawsza odpowiedź. Czy to zadziała ze starszą wersją angulara? (np. wersja 1.2)
Thariq Nugrohotomo
2
@ThariqNugrohotomo Należy pamiętać, że używanie Angular Material wymaga użycia Angular 1.3.x lub nowszej. Również dzięki za wsparcie, jestem naprawdę zdumiony wirtualnym powtórzeniem i już używamy go w aplikacji mobilnej, która wyświetla naprawdę długą listę wyników.
Sarantis Tofas
6

Inna wersja @Steffomio

Zamiast dodawać każdy element osobno, możemy dodawać elementy fragmentami.

// 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
źródło
Ciekawy pomysł. Wypróbowałem to na tablicy ~ 8000 elementów i chociaż początkowo sprawiło, że strona była bardziej responsywna, po każdym fragmencie stała się mniej responsywna.
Paul Brannan
To był ogromny problem w mojej aplikacji po ponad 500 elementach. Zamiast tego sugeruję paginację lub nieskończone ładowanie.
joalcego
0

Czasami co się stało, otrzymujesz dane z serwera (lub zaplecza) w ciągu kilku ms (na przykład zakładam, że to 100 ms), ale wyświetlenie na naszej stronie zajmuje więcej czasu (powiedzmy, że trwa 900 aby pokaz).

Tak więc to, co się tutaj dzieje, to 800 ms. Wystarczy wyrenderować stronę internetową.

To, co zrobiłem w mojej aplikacji internetowej, to użycie paginacji (lub możesz użyć nieskończonego przewijania ) do wyświetlenia listy danych. Powiedzmy, że wyświetlam 50 danych na stronę.

Więc nie będę ładował wszystkich danych na raz, tylko 50 danych, które ładuję początkowo, co zajmuje tylko 50 ms (zakładam tutaj).

więc całkowity czas tutaj zmniejszył się z 900 ms do 150 ms, gdy użytkownik zażąda następnej strony, a następnie wyświetli następne 50 danych i tak dalej.

Mam nadzieję, że pomoże to poprawić wydajność. Wszystkiego najlepszego

UniCoder
źródło
0
Created a directive (ng-repeat with lazy loading) 

który ładuje dane, gdy osiągnie dół strony i usuwa połowę wcześniej załadowanych danych, a gdy ponownie osiąga górę div, zostaną załadowane poprzednie dane (w zależności od numeru strony) usuwając połowę bieżących danych Tak na DOM w danym momencie obecne są tylko ograniczone dane, co może prowadzić do lepszej wydajności zamiast renderowania całych danych pod obciążeniem.

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

Kątowy KOD:

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 z dyrektywą

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

W zależności od wysokości podziału ładuje dane i po przewinięciu nowe dane zostaną dołączone, a poprzednie zostaną usunięte.

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

Kod kątowy:

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 z siatką UI z nieskończonym przewijaniem Demo

ankesh jain
źródło
Link do rozwiązania jest mile widziany, ale upewnij się, że Twoja odpowiedź jest przydatna bez niego: dodaj kontekst wokół linku, aby inni użytkownicy mieli pojęcie, co to jest i dlaczego się tam znajduje, a następnie zacytuj najbardziej odpowiednią część strony, którą znasz. ponowne łącze w przypadku, gdy strona docelowa jest niedostępna. Odpowiedzi, które są niewiele więcej niż linkiem, mogą zostać usunięte .
Sᴀᴍ Onᴇᴌᴀ
-2

w przypadku dużego zestawu danych i listy rozwijanej wielu wartości lepiej jest użyć ng-optionszamiast ng-repeat.

ng-repeatjest powolny, ponieważ zapętla wszystkie nadchodzące wartości, ale ng-optionspo prostu wyświetla się w opcji wyboru.

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

znacznie szybciej niż

<option ng-repeat="state in States" value="{{state.StateCode}}">
    {{state.StateName }}
</option>
Ghebrehiywet
źródło
Czy sprawdziłeś wydajność ng-options? Próbuję zoptymalizować swój kod i to nie pomogło. Szybkość jest taka sama, jak powtórzenie ng. -1
Icet
działa tylko dla zaznaczenia, powtórzenie ng jest znacznie potężniejsze. Niemniej jednak prawdą jest, że ng-Options jest znacznie szybsze niż ng-repeat. Dokumentacja AngularJs wymienia 2000 pozycji dla różnic: docs.angularjs.org/api/ng/directive/select
Kaiser