Як покращити продуктивність 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", але нічого не знайшов. Я досить новачок у кутових способах, тому можливо, що я пропускаю суть повністю. Які найкращі практики у цьому?


10
Ви дійсно хочете відобразити ВСІ рядки? Як щодо відображення лише стільки рядків, які може бачити користувач. наприклад, ви можете використовувати limitToдля відображення лише 20 елементів: <p ng-repeat="data in dataset | limitTo:20">{{data}}</p>Це відображає лише 20 елементів. Тоді ви можете скористатися сторінками і показати наступні 10 елементів або щось подібне. :)
AndreM96

для цього "звіту назад, коли він закінчує" повторення "", ви можете використовувати спеціальну директиву на додаток до ng-повтору. (Дивіться тут обраний відповідь) stackoverflow.com/questions/13471129 / ...
mayankcpdixit

передайте це питання, воно вам точно допоможе. [сюди введіть опис посилання] [1] [1]: stackoverflow.com/questions/25481021/…
Mahesh

Відповіді:


159

Я погоджуюся з @ AndreM96, що найкращим підходом є відображення лише обмеженої кількості рядків, швидшого та кращого UX, це може бути зроблено з розширенням сторінки або з нескінченною прокруткою.

Нескінченна прокрутка з кутовим дуже просто з фільтром 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 з пагінацією.


Хороша альтернатива !!! Ви знаєте будь-який метод використання, якщо я зобов’язаний показати всі предмети. будь-який знак завантаження або один за одним введенням у DOM чи щось таке?
mayankcpdixit

Ви маєте на увазі відображення "завантаження ..." чи щось під час отримання даних?
Бертран

1
@Sumit limitTo буде застосовано до ng-повторюваної області, тому результатом буде новий масив, який буде переданий до ng-повтору, ваш масив даних ще однаковий, і ви все ще можете шукати весь вміст.
Бертран

12
Якщо користувач натискає навантаження більше 10 разів і кожен натискання додає ще 100 предметів, як це може підвищити продуктивність?
hariszaman

5
@hariszaman Я згоден. Це не покращує продуктивність. Це просто затримує низьку продуктивність. Нескінченна прокрутка зашкодить вам проблеми, якщо ви не віртуалізуєте її (що робить інтерфейс).
Річард

41

Найгарячіший - і, можливо, найбільш масштабований - підхід до подолання цих викликів за допомогою великих наборів даних втілюється підходом директиви колекції Repeat Ionic та інших подібних імплементацій. Вигадним терміном для цього є "відключення оклюзії" , але ви можете підсумувати це як: не обмежуйте лише кількість виведених елементів DOM на довільне (але все-таки високе) число, що болить, наприклад, 50, 100, 500 ... , обмежте лише стільки елементів, скільки може бачити користувач .

Якщо ви робите щось на кшталт того, що зазвичай відоме як "нескінченна прокрутка", ви дещо скорочуєте початкове число DOM, але воно швидко розпускається після пари оновлень, тому що всі ці нові елементи просто приклеюються внизу. Прокрутка приходить до сканування, тому що прокрутка - все про кількість елементів. У цьому немає нічого нескінченного.

Тоді як collectionRepeatпідхід полягає у використанні лише стільки елементів, скільки вміститься у вікні перегляду, а потім їх переробці . Коли один елемент обертається з поля зору, він відривається від дерева візуалізації, поповнюється даними для нового елемента в списку, а потім приєднується до дерева візуалізації на іншому кінці списку. Це найшвидший спосіб, відомий людині для отримання нової інформації у DOM та поза нею, використовуючи обмежений набір існуючих елементів, а не традиційний цикл створення / знищення ... створення / знищення. Використовуючи такий підхід, ви можете по-справжньому реалізувати нескінченну прокрутку.

Зауважте, що вам не потрібно використовувати Ionic для використання / злому / адаптації collectionRepeatабо будь-якого іншого інструменту, як він. Тому вони називають це відкритим кодом. :-) (Отож, команда Ionic займається досить геніальними справами, вартими вашої уваги.)


Принаймні один прекрасний приклад робити щось дуже схоже в React. Тільки замість того, щоб переробляти елементи з оновленим вмістом, ви просто вирішите не відображати нічого на дереві, що не видно. Це швидко палає на 5000 предметів, хоча їх дуже проста реалізація POC дозволяє трохи мерехтіти ...


Крім того ... для повторення деяких інших публікацій, використання track byвкрай корисно, навіть із меншими наборами даних. Вважайте обов’язковим.


Дивовижна ідея команди Ionic. Цікаво, чи це походить від того, як відображаються рідні погляди?
Бредлі Флуд

Наприклад, UITableView в iOS використовує той самий підхід для візуалізації великих наборів даних. Я думаю, що це загальний підхід, який використовується у багатьох рідних поглядах.
Дмитро Котенко

36

Я рекомендую переглянути це:

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

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

Оптимізація №1: Кешування елементів DOM

Оптимізація №2: Сукупність спостерігачів

Оптимізація №3: Створення елементів відкладати

Оптимізація №4: Обхід спостерігачів для прихованих елементів

проект тут на github:

Використання:

1- включіть ці файли у свій односторінковий додаток:

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

2- залежність модуля:

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

3- замінити ng-повтор

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

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


4
Я думаю, що цей scalyr.js вже включає інші файли. Тому що це результат сценарію складання.
dnocode

Я намагався використовувати Scalyr, але фільтр не працює. <tr sly-repeat = "параметр у main.customers | filter: search_input | limitTo: 20">
aldesabido

Це надзвичайно корисно. Я використовую його в додатку AngularJS 1.6, де клієнт хоче бачити безліч комірок даних (зазвичай я розробляю форми з підказками / зменшеними елементами даних, але клієнту потрібно порівнювати безліч даних одразу). Поки сітка комірок перейшла від непридатної до ідеально чудової завдяки цій бібліотеці. Але ця версія була написана ще за два дні до AngularJS, тому я ретельно перевіряю проблеми.
флаєр

З того, що я можу сказати на даний момент, файл gatedScope.js (323 рядки) є єдиним, який потрібно перевірити, щоб бути зручним для більш сучасних версій AngularJS. Цей запит тягнути примітний: github.com/karser/angular/commit / ... . Він оновлює підпис rootScope. $ New.
флаєр

включив усі чотири файли js та використовував, sly-repeatале нічого не допомогло мені, результати все ще повільні, і браузер також затримує порушення [Violation] 'setTimeout' handler took 54ms,[Violation] 'scroll' handler took 1298ms
Gaurav Aggarwal

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/


Вам потрібні ::повтор, а також вираз? Документи говорять інакше, але я не впевнений, як перевірити, що це працює. docs.angularjs.org/guide/expression
Crhistian Ramirez

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


1
Це не дуже допомагає для продуктивності. Див jsperf.com/ng-repeat-vs-ng-repeat-with-trace-by-id
МСДЕНІ

за допомогою треку ви не відстежуєте елемент масиву з початку щоразу, коли отримуєте нові дані. в результаті це покращує ефективність роботи.
користувач1920302

2
Це корисно лише тоді, коли дані в ng-повторі змінюються. Для початкового завантаження це може не створити поліпшення продуктивності.
Сумеш Куттан

11

Якщо всі ваші рядки мають однакову висоту, вам обов'язково слід поглянути на віртуалізуючий ng-повтор: http://kamilkp.github.io/angular-vs-repeat/

Ця демонстрація виглядає дуже перспективною (і підтримує інерційну прокрутку)


2
Продуктивність прокрутки на мобільному пристрої неприйнятна (події прокрутки не працюють у мобільних iOS (лише з 8)
Johny

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

Код, що знаходиться вище, нехай відображається у списку, який зростає рядком за рядком, і завжди повільніше, ніж візуалізувати все відразу. Але для користувача це здається швидшим.


як використовувати цю функцію на html-сторінці?
Антоніс

9

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

Один із способів здійснити це - це використання кутового матеріалу, md-virtual-repeat як це демонструється на цій демонстрації з 50 000 предметів

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

Віртуальне повторення є обмеженою заміною ng-повтору, що забезпечує лише достатню кількість вузлів dom, щоб заповнити контейнер і переробити їх під час прокрутки користувача.


2
Нічого собі, я думаю, що це найцікавіша відповідь. Це буде працювати зі старшою версією кутового? (наприклад, версія 1.2)
Thariq Nugrohotomo

2
@ThariqNugrohotomo Зверніть увагу, що для використання кутового матеріалу потрібно використовувати кутовий 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 даних тощо.

Сподіваємось, це допоможе вам покращити продуктивність. Всього найкращого


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

який завантажує дані, коли він досягає нижньої частини сторінки та видаляє половину завантажених раніше даних, а коли знову доходить до вершини діла, попередні дані (залежно від номера сторінки) будуть завантажені, видаляючи половину поточних даних. Так на 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;

    }
}

Демо з сіткою інтерфейсу користувача з демонстрацією нескінченного прокрутки


Посилання на рішення вітається, але, будь ласка, переконайтеся, що ваша відповідь корисна без неї: додайте контекст навколо посилання, щоб ваші колеги користувачі мали уявлення про те, що це таке і чому воно є, а потім наведіть найбільш релевантну частину сторінки, яку ви ' повторне посилання на випадок, якщо цільова сторінка недоступна. Відповіді, які є трохи більше ніж посилання, можуть бути видалені .
Sᴀᴍ Onᴇᴌᴀ

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

Ви перевіряли працездатність ng-опцій? Я намагаюся оптимізувати свій код, і це не допомогло. Швидкість така ж, як ng-повтор. -1
Айд

працює лише для вибору, ng-повтор є набагато потужнішим. Проте правда, що ng-Options набагато швидше, ніж ng-повторення. AngularJs docs згадує 2000 пунктів для відмінностей: docs.angularjs.org/api/ng/directive/select
kaiser
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.