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

165

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

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

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

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

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

Ответы:

159

Я согласен с @ 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 с нумерацией страниц.

Bertrand
источник
Хорошая альтернатива !!! Вы знаете какой-либо метод, чтобы использовать, если я обязан показать все элементы. любой знак загрузки или одна за одной вставка в DOM или что-то?
mayankcpdixit
Вы имеете в виду отображение "загрузка ..." или что-то в то время как данные извлекаются?
Бертран
1
@Sumit limitTo будет применен к области действия ng-repeat, поэтому результатом будет новый массив, который будет передан в ng-repeat, ваш массив данных останется прежним, и вы все равно сможете искать весь контент.
Бертран
12
если пользователь нажимает loadmore 10 раз, а каждое нажатие добавляет еще 100 элементов, как это может улучшить производительность?
hariszaman
5
@hariszaman Я согласен. Это не улучшает производительность. Это просто задерживает плохую производительность. Бесконечная прокрутка доставит вам неприятности, если вы не виртуализируете ее (что делает пользовательский интерфейс).
Ричард
41

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

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

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

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


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


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

XML
источник
Отличная идея от команды Ionic. Интересно, произошло ли это из-за того, как визуализируются родные взгляды?
Брэдли Флад
Например, UITableView в iOS использует тот же подход для визуализации больших наборов данных. Я думаю, что это общий подход, используемый во многих родных взглядах.
Дмитрий Котенко
36

Я рекомендую увидеть это:

Оптимизация 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>

Наслаждаться!

pixparker
источник
4
Я думаю, что этот scalyr.js уже включает в себя другие файлы. Потому что это результат сборки скрипта.
dnocode
Я пытался использовать Scalyr, но фильтр не работает. Опция <tr sly-repeat = "в main.customers | filter: search_input | limitTo: 20">
aldesabido
Это очень полезно. Я использую его в приложении AngularJS 1.6, где клиент хочет видеть много ячеек данных (обычно я проектирую формы с элементами подкачки / уменьшенного объема данных, но клиент должен сравнивать большое количество данных одновременно). До сих пор из-за этой библиотеки сетка ячеек перешла из непригодной в идеальное состояние. Но эта библиотека была написана еще в AngularJS 1.2 дня, так что я буду тщательно тестировать ее в поисках проблем.
флаер
Из того, что я могу сказать в настоящее время, файл gatedScope.js (323 строки) - единственный, который должен быть проверен на работоспособность в более современных версиях AngularJS. Этот запрос извлечения примечателен: github.com/karser/angular/commit/… . Обновляет подпись rootScope. $ New.
флаер
включил все четыре js-файла и использовал, sly-repeatно ничего не помогло мне, результаты все еще медленны, а лаги браузера также получают нарушения [Violation] 'setTimeout' handler took 54ms,[Violation] 'scroll' handler took 1298ms
Гаурав Аггарвал
15

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

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

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

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

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

Shilan
источник
Вам нужно ::на повтор, а также выражение? Документы говорят иначе, но я не уверен, как проверить, что это работает. docs.angularjs.org/guide/expression
Кристиан Рамирес
12

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

<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

user1920302
источник
1
Это не очень помогает для производительности. См jsperf.com/ng-repeat-vs-ng-repeat-with-trace-by-id
МСДЭНИ
с помощью track by you не отслеживайте элемент массива с самого начала каждый раз, когда вы получаете новые данные В результате это улучшает производительность.
user1920302
2
Это полезно только тогда, когда данные в ng-repeat меняются. При начальной загрузке это может не привести к повышению производительности.
Сумеш Куттан
11

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

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

bartekp
источник
2
Производительность прокрутки на мобильных устройствах неприемлема (события прокрутки не запускаются на мобильных устройствах iOS (только с 8)
Джонни
9

Правило № 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
    }
}

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

Steffomio
источник
Как использовать эту функцию в HTML-странице?
Антонис
9

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

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

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

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

Сарантис Тофас
источник
2
Вау, я думаю, что это самый интересный ответ. Будет ли это работать с более старой версией Angular? (например, версия 1.2)
Тарик Нугрохотомо
2
@ThariqNugrohotomo Обратите внимание, что для использования Angular Material необходимо использовать Angular 1.3.x или выше. Также спасибо за поддержку, я действительно поражен виртуальным повторением, и мы уже используем его в мобильном приложении, которое отображает очень длинный список результатов.
Сарантис Тофас
6

Другая версия @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);
    }       
});
Луевано
источник
Интересная идея. Я попробовал это на массиве из ~ 8000 элементов, и, хотя изначально он делал страницу более отзывчивой, она становилась менее отзывчивой после каждого фрагмента.
Пол Браннан
Это было огромной проблемой в моем приложении после того, как у меня было более 500 наименований. Я предлагаю вместо этого нумерацию страниц или бесконечную загрузку.
Joalcego
0

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

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

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

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

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

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

UniCoder
источник
0
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="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>

Угловой код:

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="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>

Угловой код:

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;

    }
}

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

анкеш джайн
источник
Ссылка на решение приветствуется, но, пожалуйста, убедитесь, что ваш ответ будет полезен без него: добавьте контекст вокруг ссылки, чтобы ваши коллеги-пользователи поняли, что это такое и почему, а затем процитируйте наиболее релевантную часть страницы, которую вы ' Повторная ссылка на случай, если целевая страница недоступна. Ответы, которые немного больше, чем ссылка, могут быть удалены .
Суббота,
-2

для большого набора данных и раскрывающегося множества значений лучше использовать 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>
Ghebrehiywet
источник
Вы проверяли производительность ng-options? Я пытаюсь оптимизировать мой код, и это не помогло. Скорость равна ng-repeat. -1
Icet
работает только для select, ng-repeat более мощный. Тем не менее, это правда, что ng-Options намного быстрее, чем ng-repeat. AngularJs docs упоминает 2000 пунктов для различий: docs.angularjs.org/api/ng/directive/select
kaiser