Как повысить производительность ngRepeat на большом наборе данных (angular.js)?

У меня есть огромный набор данных из нескольких тысяч строк с примерно 10 полями в каждой, около 2 МБ данных. Мне нужно отобразить его в браузере. Самый простой подход (извлечь данные, поместить их в $scope, позволить ng-repeat="" сделать свою работу) работает нормально, но браузер зависает примерно на полминуты, когда он начинает вставлять узлы в DOM. Как мне подойти к этой проблеме?

Один из вариантов — постепенно добавлять строки в $scope и ждать, пока ngRepeat завершит вставку одного фрагмента в DOM, прежде чем переходить к следующему. Но AFAIK ngRepeat не сообщает, когда заканчивает «повторение», так что это будет некрасиво.

Другой вариант — разделить данные на сервере на страницы и получить их несколькими запросами, но это еще более уродливо.

Я просмотрел документацию по Angular в поисках чего-то вроде ng-repeat="data in dataset" ng-repeat-steps="500", но ничего не нашел. Я новичок в Angular, поэтому вполне возможно, что я полностью упускаю суть. Каковы лучшие практики в этом?


person n1313    schedule 27.06.2013    source источник
comment
Вы действительно хотите отображать ВСЕ строки? Как насчет отображения только того количества строк, которое может видеть пользователь? например вы можете использовать limitTo для отображения только 20 элементов: <p ng-repeat="data in dataset | limitTo:20">{{data}}</p> Это показывает только 20 элементов. Затем вы можете использовать страницы и показать следующие 10 элементов или что-то в этом роде. :)   -  person AndreM96    schedule 27.06.2013
comment
для этого отчета, когда он заканчивает «повторение», вы можете использовать пользовательскую директиву в дополнение к ng-repeat. (см. здесь выбранный ответ) stackoverflow.com/questions/ 13471129/   -  person mayankcpdixit    schedule 10.01.2014
comment
обратитесь к этому вопросу, это обязательно поможет вам. [введите описание ссылки здесь][1] [1]: stackoverflow.com/questions/25481021/   -  person Mahesh    schedule 26.02.2015


Ответы (12)


Я согласен с @AndreM96 в том, что лучший подход - отображать только ограниченное количество строк, быстрее и лучше UX, это можно сделать с разбиением на страницы или с бесконечной прокруткой.

Бесконечная прокрутка с Angular очень проста с фильтром limitTo. Вам просто нужно установить начальный лимит, и когда пользователь запрашивает дополнительные данные (для простоты я использую кнопку), вы увеличиваете лимит.

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

Вот JsBin.

Такой подход может быть проблемой для телефонов, потому что обычно они отстают при прокрутке большого количества данных, поэтому в этом случае я думаю, что нумерация страниц подходит лучше.

Для этого вам понадобится фильтр limitTo, а также пользовательский фильтр для определения начальной точки отображаемых данных.

Вот JSBin с нумерацией страниц.

person Bertrand    schedule 27.06.2013
comment
Хорошая альтернатива!!! Вы знаете, какой метод использовать, если я обязан показать все предметы. любой знак загрузки или одна за другой вставка в DOM или что-то в этом роде? - person mayankcpdixit; 10.01.2014
comment
Вы имеете в виду отображать загрузку... или что-то еще, пока данные извлекаются? - person Bertrand; 10.01.2014
comment
Можете ли вы привести пример загрузки данных при прокрутке? Я не хочу, чтобы пользователь нажимал кнопку загрузки дополнительных данных, скорее было бы лучше загружать данные, когда пользователь достигает конца прокрутки. Я пытался найти вещи, но не нашел подходящего решения с угловым. - person Hemant Metalia; 17.06.2015
comment
будет ли фильтр теперь искать только содержимое массива, ограниченного limitTo, или он будет искать исходный большой массив? - person ProblemsOfSumit; 04.08.2015
comment
@Sumit limitTo будет применяться к области ng-repeat, поэтому результатом будет новый массив, который будет передан в ng-repeat, ваш массив данных останется прежним, и вы все равно сможете искать весь контент. - person Bertrand; 04.08.2015
comment
если пользователь нажимает loadmore 10 раз, и каждое нажатие добавляет еще 100 элементов, как это может улучшить производительность? - person hariszaman; 10.09.2015
comment
@hariszaman Я согласен. Это не улучшает производительность. Это просто задерживает плохую производительность. Бесконечная прокрутка вызовет у вас проблемы, если вы не виртуализируете ее (что делает ui-grid). - person richard; 10.12.2015
comment
Hariszaman Возможно, вы захотите пересмотреть дизайн своего приложения, если для каждого запроса loadMore вы должны выводить сотни новых элементов в представление, просмотр списка с тысячами элементов выглядит не очень человечно, кроме того, дросселирование — это способ решить проблему с несколькими последовательными кликами, в любом случае, если вам нужно отобразить тысячи элементов, я согласен с Ричард, что ui-grid, вероятно, хороший инструмент. - person Bertrand; 25.09.2016
comment
Бесконечная прокрутка не работает в iOS, но отлично работает в Интернете. Я ничего не пропустил для iOS? - person Warrior; 08.11.2016

Самый популярный и, возможно, наиболее масштабируемый подход к преодолению этих проблем с большими наборами данных воплощен в подходе директивы Ionic collectionRepeat и других подобных реализаций. Причудливый термин для этого — "отсечение окклюзии", но вы можете резюмировать это следующим образом: не просто ограничивайте количество отображаемых элементов DOM до произвольного (но все же большого) числа с разбивкой на страницы, например 50, 100, 500... вместо этого ограничьте только столько элементов, сколько может видеть пользователь.

Если вы делаете что-то вроде того, что обычно называют «бесконечной прокруткой», вы несколько уменьшаете начальный счетчик DOM, но он быстро раздувается после пары обновлений, потому что все эти новые элементы просто добавляются в дно. Прокрутка сводится к обходу, потому что прокрутка — это количество элементов. В этом нет ничего бесконечного.

Принимая во внимание, что подход collectionRepeat состоит в том, чтобы использовать столько элементов, сколько поместится в области просмотра, а затем перерабатывать их. Когда один элемент выходит из поля зрения, он отсоединяется от дерева рендеринга, снова заполняется данными для нового элемента в списке, а затем снова прикрепляется к дереву рендеринга на другом конце списка. Это самый быстрый из известных человеку способов получения новой информации в DOM и из нее, используя ограниченный набор существующих элементов, а не традиционный цикл создания/уничтожения... создания/уничтожения. Используя этот подход, вы действительно можете реализовать бесконечную прокрутку.

Обратите внимание, что вам не нужно использовать Ionic, чтобы использовать/взламывать/адаптировать collectionRepeat или любой другой подобный инструмент. Вот почему они называют это открытым исходным кодом. :-) (Тем не менее, команда Ionic делает довольно оригинальные вещи, достойные вашего внимания.)


Есть по крайней мере один отличный пример выполнения чего-то очень похожего в React. . Только вместо того, чтобы перерабатывать элементы с обновленным содержимым, вы просто решаете не отображать в дереве ничего, чего нет в поле зрения. Он молниеносно работает на 5000 элементов, хотя их очень простая реализация POC позволяет немного мерцать...


Кроме того... чтобы повторить некоторые другие сообщения, использование track by очень полезно, даже с меньшими наборами данных. Считайте обязательным.

person XML    schedule 12.06.2015
comment
Потрясающая идея от команды Ionic. Интересно, произошло ли это из-за того, как отображаются нативные представления? - person Bradley Flood; 15.06.2015
comment
Например, UITableView в iOS использует тот же подход для рендеринга больших наборов данных. Я думаю, что это общий подход, используемый во многих нативных представлениях. - person Dmitry Kotenko; 09.09.2016

Рекомендую посмотреть это:

Оптимизация AngularJS: с 1200 мс до 35 мс

они сделали новую директиву, оптимизировав ng-repeat в 4 частях:

Оптимизация №1: кешировать элементы DOM

Оптимизация № 2: совокупные наблюдатели

Оптимизация № 3: отложить создание элемента

Оптимизация № 4: Обход наблюдателей для скрытых элементов

проект находится здесь на github:

Применение:

1- включите эти файлы в одностраничное приложение:

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

2- добавить зависимость модуля:

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

3- заменить нг-повторить

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

Наслаждайтесь!

person pixparker    schedule 31.05.2015
comment
Я думаю, что этот scalyr.js уже включает в себя другие файлы. Потому что это результат сборки скрипта. - person dnocode; 03.02.2016
comment
Я пытался использовать Scalyr, но фильтр не работает. ‹tr sly-repeat=опция в main.customers | фильтр: search_input | limitTo:20› - person aldesabido; 10.01.2017
comment
Это очень полезно. Я использую его в приложении AngularJS 1.6, где клиент хочет видеть множество ячеек данных (обычно я разрабатываю формы с разбиением на страницы/уменьшением элементов данных, но клиенту нужно сравнивать множество данных одновременно). До сих пор сетка ячеек превратилась из непригодной в совершенно прекрасную благодаря этой библиотеке. Но эта библиотека была написана еще во времена AngularJS 1.2, поэтому я буду тщательно тестировать ее в поисках проблем. - person flyer; 04.08.2017
comment
Из того, что я могу сказать на данный момент, файл gatedScope.js (323 строки) — единственный, который необходимо проверить для запуска в более поздних версиях AngularJS. Этот пул-реквест примечателен: github.com/karser/angular/commit/. Он обновляет сигнатуру rootScope.$new. - person flyer; 08.08.2017
comment
включил все четыре файла js и использовал sly-repeat, но мне ничего не помогло, результаты по-прежнему медленные, а браузер лагает, также возникают нарушения [Violation] 'setTimeout' handler took 54ms, [Violation] 'scroll' handler took 1298ms - person Gaurav Aggarwal; 06.02.2018

Помимо всех вышеперечисленных подсказок, таких как отслеживание и меньшие циклы, этот также очень помог мне.

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

этот фрагмент кода будет печатать имя после его загрузки и после этого перестанет его просматривать. Точно так же для ng-repeats его можно использовать как

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

однако он работает только для AngularJS версии 1.3 и выше. Из http://www.befundoo.com/blog/optimizing-ng-repeat-in-angularjs/

person Shilan    schedule 27.11.2015
comment
Вам нужен :: на повторе, а также выражение? Документы говорят об обратном, но я не уверен, как проверить, работает ли это. docs.angularjs.org/guide/expression - person Crhistian Ramirez; 27.12.2016
comment
:: является избыточным. ng-bind используется для односторонней привязки данных. - person LazioTibijczyk; 17.08.2020

Вы можете использовать «track by», чтобы увеличить производительность:

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

Быстрее, чем:

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

ссылка: https://www.airpair.com/angularjs/posts/angularjs-performance-large-applications

person user1920302    schedule 01.05.2015
comment
Это не очень помогает для производительности. См. jsperf.com/ng-repeat-vs-ng -повторить-с-трассировкой-по-идентификатору - person ilter; 04.06.2015
comment
с отслеживанием вы не отслеживаете элемент массива с самого начала каждый раз, когда получаете новые данные. в результате это улучшает производительность. - person user1920302; 05.11.2015
comment
Это полезно только тогда, когда данные в ng-repeat меняются. Для начальной загрузки это может не привести к повышению производительности. - person Sumesh Kuttan; 03.02.2016

Если все ваши строки имеют одинаковую высоту, вам обязательно стоит взглянуть на виртуализацию ng-repeat: http://kamilkp.github.io/angular-vs-repeat/

Эта демонстрация выглядит очень многообещающе (и поддерживает инерционную прокрутку).

person bartekp    schedule 20.04.2014
comment
Производительность прокрутки на мобильных устройствах неприемлема (события прокрутки не запускаются на мобильных устройствах iOS (только начиная с 8) - person Johny; 03.10.2014

Виртуальная прокрутка – это еще один способ повысить производительность прокрутки при работе с огромными списками и большими наборами данных.

Один из способов реализовать это — использовать материал Angular md-virtual-repeat, как показано на этом Демо с 50 000 элементов

Взято прямо из документации виртуального повтора:

Виртуальный повтор — это ограниченная замена ng-repeat, которая отображает ровно столько dom-узлов, сколько нужно для заполнения контейнера, и перерабатывает их по мере прокрутки пользователем.

person Sarantis Tofas    schedule 15.04.2016
comment
Вау, я думаю, это самый интересный ответ. Будет ли это работать со старой версией angular? (например, версия 1.2) - person Thariq Nugrohotomo; 23.08.2016
comment
@ThariqNugrohotomo Обратите внимание, что для использования Angular Material требуется использование Angular 1.3.x или выше. Также спасибо за поддержку, я действительно поражен виртуальным повтором, и мы уже используем его в мобильном приложении, которое отображает очень длинный список результатов. - person Sarantis Tofas; 23.08.2016

Правило №1: Никогда не позволяйте пользователю ничего ждать.

Имея в виду, что растущая страница, которой нужно 10 секунд, появляется намного быстрее, чем ожидание 3 секунд до пустого экрана и получение всего сразу.

Таким образом, вместо того, чтобы сделать страницу быстрой, просто позвольте странице кажуться быстрой, даже если конечный результат будет медленнее:

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

В приведенном выше коде создается впечатление, что список растет построчно, и это всегда медленнее, чем рендеринг всего сразу. Но для пользователя это кажется быстрее.

person Steffomio    schedule 20.04.2016
comment
как использовать эту функцию на странице html? - person Antonis; 27.11.2016

Другая версия @Steffomio

Вместо того, чтобы добавлять каждый элемент по отдельности, мы можем добавлять элементы по частям.

// 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);
    }       
});
person Luevano    schedule 11.07.2016
comment
Интересная идея. Я попробовал это на массиве примерно из 8000 элементов, и хотя изначально это сделало страницу более отзывчивой, она становилась менее отзывчивой после каждого фрагмента. - person Paul Brannan; 16.08.2019
comment
Это была огромная проблема в моем приложении после того, как у меня было более 500 элементов. Вместо этого я предлагаю нумерацию страниц или бесконечную загрузку. - person joalcego; 07.04.2020

Иногда случалось, что вы получаете данные с сервера (или серверной части) за несколько мс (например, я предполагаю, что это 100 мс), но для отображения в нашем веб-страница (допустим, для ее отображения требуется 900 мс).

Итак, то, что здесь происходит, составляет 800 мс. Требуется только для отображения веб-страницы.

В своем веб-приложении я использовал нумерацию страниц (или вы также можете использовать бесконечную прокрутку) для отображения списка данных. Допустим, я показываю 50 данных/страницу.

Поэтому я не буду загружать все данные сразу, я загружаю только 50 данных, что занимает всего 50 мс (здесь я предполагаю).

поэтому общее время здесь уменьшилось с 900 мс до 150 мс, когда пользователь запрашивает следующую страницу, затем отображает следующие 50 данных и так далее.

Надеюсь, это поможет вам повысить производительность. Всего наилучшего

person UniCoder    schedule 08.02.2017

Created a directive (ng-repeat with lazy loading) 

который загружает данные, когда они достигают нижней части страницы, и удаляют половину ранее загруженных данных, а когда они снова достигают верхней части div, будут загружаться предыдущие данные (в зависимости от номера страницы), удаляя половину текущих данных Итак, в DOM одновременно присутствуют только ограниченные данные, что может привести к повышению производительности вместо рендеринга полных данных при загрузке.

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>

Угловой КОД:

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

Демонстрация с директивой

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

В зависимости от высоты раздела он загружает данные, и при прокрутке будут добавлены новые данные, а предыдущие данные будут удалены.

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>

Угловой код:

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;

    }
}

Демонстрация с сеткой пользовательского интерфейса и бесконечной прокруткой

person ankesh jain    schedule 08.09.2017
comment
Ссылка на решение приветствуется, но убедитесь, что ваш ответ полезен и без нее: добавьте контекст вокруг ссылки, чтобы ваш коллега пользователи будут иметь некоторое представление о том, что это такое и почему оно там, а затем процитируйте наиболее важную часть страницы, на которую вы ссылаетесь, в случае, если целевая страница недоступна. Ответы, которые представляют собой не более чем ссылку, могут быть удалены. - person Sᴀᴍ Onᴇᴌᴀ; 08.09.2017

для большого набора данных и раскрывающегося списка с несколькими значениями лучше использовать ng-options, а не ng-repeat.

ng-repeat работает медленно, потому что перебирает все поступающие значения, а ng-options просто отображает опцию выбора.

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

гораздо быстрее, чем

<option ng-repeat="state in States" value="{{state.StateCode}}">
    {{state.StateName }}
</option>
person Ghebrehiywet    schedule 16.05.2016
comment
Вы проверяли производительность ng-options? Я пытаюсь оптимизировать свой код, и это не помогло. Скорость такая же, как у ng-repeat. -1 - person Icet; 04.01.2017
comment
работает только для выбора, ng-repeat намного мощнее. Тем не менее, это правда, что ng-Options намного быстрее, чем ng-repeat. В документах AngularJs упоминается 2000 элементов различий: docs.angularjs.org/api/ng/directive/ выбрать - person kaiser; 21.11.2018