거대한 데이터 세트 (angular.js)에서 ngRepeat의 성능을 향상시키는 방법은 무엇입니까?


165

약 10MB의 필드, 약 2MB의 데이터가있는 수천 행의 거대한 데이터 세트가 있습니다. 브라우저에 표시해야합니다. 가장 간단한 접근 방식 (데이터 가져 오기, 넣기 $scope, ng-repeat=""작업 수행)은 제대로 작동하지만 DOM에 노드 삽입을 시작하면 약 0.5 분 동안 브라우저가 정지됩니다. 이 문제에 어떻게 접근해야합니까?

한 가지 옵션은 $scope점진적으로 행을 추가 ngRepeat하고 한 청크를 DOM에 삽입 하기 를 기다렸다가 다음 행으로 이동하는 것입니다. 그러나 AFAIK ngRepeat는 "반복"이 끝나면 다시보고하지 않으므로 추악합니다.

다른 옵션은 서버의 데이터를 페이지로 분할하여 여러 요청에서 가져 오는 것이지만 훨씬 더 나쁩니다.

과 같은 것을 찾기 위해 Angular 설명서를 살펴 보았지만 ng-repeat="data in dataset" ng-repeat-steps="500"아무것도 찾지 못했습니다. 나는 Angular 방식에 상당히 익숙하지 않으므로 포인트가 완전히 누락되었을 수 있습니다. 이 모범 사례는 무엇입니까?


10
정말로 모든 행을 표시 하시겠습니까? 사용자가 볼 수있는 많은 행만 표시하는 것은 어떻습니까? 예를 들어 limitTo20 개 항목 만 표시 하는 데 사용할 수 있습니다 <p ng-repeat="data in dataset | limitTo:20">{{data}}</p>. 20 개 항목 만 표시됩니다. 그런 다음 페이지를 사용하여 다음 10 개 항목 또는 이와 유사한 항목을 표시 할 수 있습니다. :)
AndreM96 2016 년

" '반복'이 끝나면 다시보고"하는 것 때문에 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 이 있습니다.


좋은 대안 !!! 모든 항목을 표시해야 할 경우 사용할 방법을 알고 있습니다. DOM 또는 무언가에 삽입 한 후로드 기호 또는 하나?
mayankcpdixit

데이터를 가져 오는 동안 "로드 중 ..."또는 무언가를 표시한다는 의미입니까?
Bertrand

1
@Sumit limitTo는 ng-repeat 범위에 적용되므로 결과는 ng-repeat로 전달되는 새 배열이되며 데이터 배열은 여전히 ​​동일하며 여전히 모든 내용을 검색 할 수 있습니다.
Bertrand

12
사용자가 10 번 이상로드를 누르고 매번 100 개 이상의 항목을 추가하면 어떻게 성능을 향상시킬 수 있습니까?
hariszaman

5
@hariszaman 동의합니다. 이것은 성능을 향상시키지 않습니다. 성능 저하 만 지연시킵니다. 무한 스크롤은 가상화하지 않는 한 (ui-grid 가하는) 문제가 발생합니다.
richard dec

41

대규모 데이터 세트로 이러한 과제를 극복하기위한 가장 인기 있고 확장 성이 뛰어난 방법은 Ionic의 collectionRepeat 지시문 및 이와 유사한 다른 구현 방식으로 구현됩니다. 이것에 대한 멋진 용어는 '오 클루 전 컬링' 이지만, 요약하면 렌더링 된 DOM 요소의 수를 50, 100, 500과 같은 임의의 (그러나 여전히 높은) 페이지 매김 된 숫자로 제한하지 마십시오 ... , 제한 사용자 만 많은 요소를 볼 수에 .

일반적으로 "무한 스크롤"로 알려진 것과 같은 작업을 수행하는 경우 초기 DOM 수를 약간 줄이려고하지만 몇 가지 새로 고침 후에는 모든 새 요소가 맨 아래에 고정되어 있기 때문에 빠르게 증가합니다. 스크롤은 요소 수에 관한 것이기 때문에 스크롤이 크롤링됩니다. 그것에 대해 무한한 것은 없습니다.

반면, collectionRepeat접근 방식은 뷰포트에 맞는만큼의 요소 만 사용한 다음 재활용하는 것 입니다. 한 요소가 시야에서 벗어나면 렌더 트리에서 분리되어 목록의 새 항목에 대한 데이터로 다시 채워진 다음 목록의 다른 쪽 끝에있는 렌더 트리에 다시 연결됩니다. 이것은 사람이 DOM으로 들어오고 나가는 새로운 정보를 얻는 가장 빠른 방법으로, 기존의 생성 / 파괴주기 인 생성 / 파괴주기보다는 제한된 기존 요소 세트를 사용합니다. 이 방법을 사용하면 무한 스크롤을 실제로 구현할 수 있습니다 .

Ionic을 사용하여 / hack / adapt collectionRepeat또는 이와 유사한 도구 를 사용할 필요는 없습니다 . 이것이 그들이 오픈 소스라고 부르는 이유입니다. :-) (이온 팀은주의를 기울일만한 아주 독창적 인 일을하고 있습니다.)


React에서 매우 비슷한 것을하는 훌륭한 예가 적어도 하나 있습니다. 업데이트 된 컨텐츠가있는 요소를 재활용하는 대신 트리에서 보이지 않는 것을 렌더링하지 않기로 선택하는 것입니다. 매우 간단한 POC 구현으로 약간의 깜박임이 허용되지만 5000 항목에서 빠르게 타 오르고 있습니다 ...


또한 ... 다른 게시물 중 일부를 반향하려면 track by작은 데이터 세트에서도 사용하는 것이 매우 유용합니다. 필수라고 생각하십시오.


이온 팀의 멋진 아이디어. 네이티브 뷰가 렌더링되는 방식에서 나온 것인지 궁금합니다.
Bradley Flood

예를 들어 iOS의 UITableView는 동일한 접근 방식을 사용하여 대용량 데이터 세트를 렌더링합니다. 나는 이것이 많은 기본 견해에서 사용되는 일반적인 접근법이라고 생각합니다.
Dmitry Kotenko

36

나는 이것을 볼 것을 권장합니다 :

AngularJS 최적화 : 1200ms ~ 35ms

그들은 네 부분으로 ng-repeat를 최적화함으로써 새로운 지시를 내 렸습니다.

최적화 # 1 : 캐시 DOM 요소

최적화 # 2 : 집계 감시자

최적화 # 3 : 요소 생성 지연

최적화 # 4 : 숨겨진 요소에 대한 감시자 우회

프로젝트는 github에 있습니다.

용법:

1-이 파일들을 단일 페이지 앱에 포함 시키십시오 :

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

2- 모듈 의존성 추가 :

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

3- ng-repeat 교체

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

즐겨!


4
이 scalyr.js에는 이미 기타 파일이 포함되어 있다고 생각합니다. 빌드 스크립트의 결과이기 때문입니다.
dnocode

Scalyr을 사용하려고했지만 필터가 작동하지 않습니다. <tr sly-repeat = "main.customers 옵션 | 필터 : search_input | limitTo : 20">
aldesabido

이것은 매우 도움이됩니다. 클라이언트가 많은 데이터 셀을보고 싶어하는 AngularJS 1.6 앱에서 사용하고 있습니다 (일반적으로 페이징 / 축소 된 데이터 요소로 양식을 디자인하지만 클라이언트는 한 번에 많은 데이터를 비교해야합니다). 지금까지는이 라이브러리로 인해 셀 그리드가 사용할 수없는 상태에서 완벽하게 미세하게되었습니다. 그러나이 lib는 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
Gaurav 가르 왈

15

트랙 바이 및 더 작은 루프와 같은 위의 모든 힌트 외에도이 힌트는 많은 도움이되었습니다.

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


당신이 필요하십니까 ::반복뿐만 아니라 표현에? 문서가 다르게 말하지만 이것이 작동하는지 테스트 할 방법이 확실하지 않습니다. 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
ilter

by by track을 사용하면 새 데이터를 얻을 때마다 처음부터 배열 요소를 추적하지 않습니다. 결과적으로 성능이 향상됩니다.
user1920302

2
이것은 ng-repeat의 데이터가 변경 될 때만 유용합니다. 초기로드의 경우 성능이 향상되지 않을 수 있습니다.
Sumesh Kuttan

11

모든 행의 높이가 동일한 경우 가상화 ng-repeat를 확인해야합니다. http://kamilkp.github.io/angular-vs-repeat/

데모 는 매우 유망 해 보이며 관성 스크롤을 지원합니다.


2
모바일에서 스크롤 성능이 허용되지 않습니다 (스크롤 이벤트는 모바일 iOS에서 실행되지 않습니다 (8 개만)
Johny

9

규칙 1 : 사용자가 아무것도 기다리지 않도록하십시오.

빈 화면이 나타나기 전에 3 초를 기다리는 것보다 10 초가 걸리는 수명이 늘어나는 페이지가 더 빨리 나타나고 한 번에 모두 얻을 수 있습니다.

따라서 페이지를 빠르게 만드는 대신 최종 결과가 느리더라도 페이지 를 빠르게 표시 하십시오 .

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 페이지에서이 기능을 사용하는 방법?
Antonis

9

가상 스크롤 은 거대한 목록과 큰 데이터 세트를 처리 할 때 스크롤 성능을 향상시키는 또 다른 방법입니다.

이를 구현하는 한 가지 방법 은이 데모에서 50,000 개의 항목으로 설명 된대로 Angular Material md-virtual-repeat 을 사용하는 것입니다.

가상 반복 문서에서 바로 가져옵니다.

가상 반복은 컨테이너를 채우고 사용자가 스크롤 할 때 컨테이너를 채우고 재활용 할 수있는 충분한 dom 노드 만 렌더링하는 ng-repeat의 제한된 대체입니다.


2
와우, 이것이 가장 흥미로운 답변이라고 생각합니다. 이전 버전의 각도에서도 작동합니까? (예 : 1.2)
Thariq Nugrohotomo

2
@ThariqNugrohotomo Angular 머티리얼을 사용하려면 Angular 1.3.x 이상이 필요합니다. 또한 지원 덕분에 가상 반복에 정말 놀랐고 이미 긴 결과 목록을 표시하는 모바일 앱에서 이미 사용하고 있습니다.
Sarantis Tofas

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 요소의 배열 에서이 작업을 시도했지만 처음에는 페이지의 응답 속도를 높이는 반면 각 청크 후에는 응답 성이 떨어졌습니다.
Paul Brannan

500 개가 넘는 항목을 보유한 후 내 앱에서 큰 문제였습니다. 대신 페이지 매김 또는 무한 로딩을 제안합니다.
joalcego

0

때때로 무슨 일이 있었는지, 당신은 몇 MS의 서버에서 데이터 (또는 백 엔드)를 취득 (예를 들어 내가이 100ms로 가정하고있어)하지만 그것은 우리의 웹 페이지에 표시 할 시간이 더 걸립니다 (의가가 900ms를 복용하고 있다고하자 디스플레이).

그래서 여기서 일어나는 일은 800ms입니다. 웹 페이지를 렌더링하는 데 걸리는 것입니다.

웹 응용 프로그램에서 수행 한 작업은 페이지 매김 을 사용 하여 데이터 목록을 표시 하는 것입니다 (또는 무한 스크롤을 사용할 수도 있음). 페이지 당 50 개의 데이터를 표시한다고 가정 해 보겠습니다.

따라서 모든 데이터를 한 번에로드 렌더링하지 않고 처음로드하는 50 개의 데이터 만 50ms 만 걸립니다 (여기서는 가정합니다).

사용자가 다음 페이지를 요청한 후 다음 50 개의 데이터를 표시하는 등 총 시간이 900ms에서 150ms로 줄었습니다.

이것이 성능 향상에 도움이되기를 바랍니다. 모두 제일 좋다


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

페이지의 하단에 도달하고 이전에로드 된 데이터의 절반을 제거 할 때 데이터를로드하고 다시 div의 상단에 도달하면 이전 데이터 (페이지 번호에 따라 다름)가 현재 데이터의 절반을 제거하여로드됩니다. 한 번에 제한된 데이터 만 존재하므로로드시 전체 데이터를 렌더링하는 대신 성능이 향상 될 수 있습니다.

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;

    }
}

무한 스크롤 데모가있는 UI 그리드를 사용한 데모


솔루션에 대한 링크는 환영하지만 답변없이 유용한 답변을 얻으십시오 . 링크 주위에 컨텍스트를 추가 하여 동료 사용자가 그 이유와 그 이유를 파악한 다음 페이지의 가장 관련성이 높은 부분을 인용하십시오. 대상 페이지를 사용할 수없는 경우 다시 연결 링크 이상인 답변은 삭제 될 수 있습니다 .
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-options의 성능을 확인 했습니까? 코드를 최적화하려고하는데 도움이되지 않았습니다. 속도는 ng-repeat와 동일합니다. -1
Icet

선택에 대해서만 작동합니다. ng-repeat가 훨씬 강력합니다. 그럼에도 불구하고 ng-Options가 ng-repeat보다 훨씬 빠릅니다. AngularJs 문서는 차이점에 대한 2000 가지 항목을 언급합니다 : docs.angularjs.org/api/ng/directive/select
kaiser
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.