วิธีปรับปรุงประสิทธิภาพของ ngRepeat ผ่านชุดข้อมูลขนาดใหญ่ (angular.js)


165

ฉันมีชุดข้อมูลขนาดใหญ่หลายพันแถวโดยมีฟิลด์ประมาณ 10 ฟิลด์แต่ละแห่งมีข้อมูลประมาณ 2MB ฉันต้องแสดงมันในเบราว์เซอร์ วิธีการที่ตรงไปตรงมาส่วนใหญ่ (ดึงข้อมูลใส่เข้าไป$scopeให้ng-repeat=""งานทำ) ทำงานได้ดี แต่จะหยุดเบราว์เซอร์ประมาณครึ่งนาทีเมื่อมันเริ่มแทรกโหนดใน DOM ฉันจะแก้ไขปัญหานี้ได้อย่างไร

ทางเลือกหนึ่งคือการผนวกแถวเป็นส่วน$scopeเพิ่มและรอngRepeatให้แทรกก้อนอันหนึ่งเข้าไปใน DOM ให้เสร็จก่อนที่จะย้ายไปยังแถวถัดไป แต่ AFAIK ngRepeat จะไม่รายงานกลับเมื่อเสร็จสิ้น "การทำซ้ำ" ดังนั้นมันจะน่าเกลียด

ตัวเลือกอื่นคือการแบ่งข้อมูลบนเซิร์ฟเวอร์ออกเป็นหน้าต่างๆและดึงข้อมูลออกมาในหลาย ๆ คำร้องขอ

ฉันดูเอกสารเชิงมุมในการค้นหาสิ่งที่ต้องการ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-repeat (ดูคำตอบที่เลือกไว้ที่นี่) 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

คุณหมายถึงแสดง "กำลังโหลด ... " หรือบางสิ่งในขณะที่กำลังดึงข้อมูลหรือไม่
Bertrand

1
@Sumit limitTo จะถูกใช้กับขอบเขต ng-repeat ดังนั้นผลลัพธ์จะเป็นอาร์เรย์ใหม่ซึ่งจะถูกส่งไปยัง ng-repeat อาร์เรย์ข้อมูลของคุณยังคงเหมือนเดิมและคุณยังสามารถค้นหาเนื้อหาทั้งหมดได้
Bertrand

12
หากผู้ใช้กดโหลดเพิ่มเติม 10 ครั้งและทุกครั้งที่กดเพิ่ม 100 รายการสิ่งนี้จะปรับปรุงประสิทธิภาพได้อย่างไร
hariszaman

5
@hariszaman ฉันเห็นด้วย สิ่งนี้ไม่ปรับปรุงประสิทธิภาพ มันเพียงแค่ชะลอการทำงานที่ไม่ดี การเลื่อนแบบไม่มีที่สิ้นสุดจะทำให้คุณเดือดร้อนเว้นแต่ว่าคุณกำลังจำลองระบบเสมือนจริง (ซึ่ง ui-grid ทำ)
richard

41

วิธีที่ร้อนแรงที่สุดและปรับขนาดได้มากที่สุดในการเอาชนะความท้าทายเหล่านี้ด้วยชุดข้อมูลขนาดใหญ่นั้นได้รับการรวบรวมโดยแนวทางการรวบรวมคำสั่งการทำซ้ำของIonicและการนำไปปฏิบัติอื่น ๆ เช่นนั้น เทอมแฟนซีสำหรับสิ่งนี้คือ'การบดเคี้ยวแบบแยกส่วน'แต่คุณสามารถรวมมันเป็น: อย่าเพิ่ง จำกัด จำนวนองค์ประกอบ DOM ที่เรนเดอร์ไปที่จำนวนเลขหน้าโดยพลการ (แต่ยังคงสูง) เช่น 50, 100, 500 ... แทน , ขีด จำกัด เท่านั้นที่จะเป็นองค์ประกอบมากที่สุดเท่าที่ผู้ใช้สามารถดูเท่านั้นที่จะเป็นองค์ประกอบมากที่สุดเท่าที่ผู้ใช้สามารถดู

หากคุณทำสิ่งที่ชอบเรียกว่า "การเลื่อนแบบไม่สิ้นสุด" คุณจะลดค่าเริ่มต้นลงจำนวน DOMบ้าง แต่มันจะขยายอย่างรวดเร็วหลังจากการรีเฟรชสองสามครั้งเนื่องจากองค์ประกอบใหม่ทั้งหมดนั้นเพิ่งถูกตรึงที่ด้านล่าง การเลื่อนมาถึงการรวบรวมข้อมูลเนื่องจากการเลื่อนทั้งหมดเกี่ยวกับการนับองค์ประกอบ ไม่มีที่สิ้นสุดเกี่ยวกับเรื่องนี้

ขณะที่collectionRepeatวิธีการคือการใช้เพียงเป็นองค์ประกอบมากที่สุดเท่าที่จะพอดีกับวิวพอร์ตแล้วรีไซเคิลพวกเขา เมื่อองค์ประกอบหนึ่งหมุนออกจากมุมมองจึงแยกออกจากโครงสร้างการแสดงผลเติมข้อมูลด้วยรายการใหม่ในรายการจากนั้นแนบมาที่ต้นไม้การแสดงผลที่ปลายอีกด้านของรายการ นี่เป็นวิธีที่เร็วที่สุดที่มนุษย์รู้จักเพื่อรับข้อมูลใหม่เข้าและออกจาก DOM ใช้ชุดองค์ประกอบที่มีอยู่อย่าง จำกัด แทนที่จะใช้วงจรการสร้าง / ทำลายแบบดั้งเดิม ... สร้าง / ทำลาย เมื่อใช้วิธีนี้คุณจะสามารถใช้การเลื่อนแบบไม่มีที่สิ้นสุดอย่างแท้จริง

โปรดทราบว่าคุณไม่จำเป็นต้องใช้ไอออนิกในการใช้ / แฮ็ก / ดัดแปลงcollectionRepeatหรือเครื่องมืออื่น ๆ เช่นมัน นั่นเป็นเหตุผลที่พวกเขาเรียกมันว่าโอเพ่นซอร์ส :-) (ที่กล่าวว่าทีมอิออนกำลังทำสิ่งที่แยบยลและน่าสนใจ


มีอย่างน้อยหนึ่งตัวอย่างที่ยอดเยี่ยมของการทำสิ่งที่คล้ายกันมากในการตอบสนอง แทนที่จะรีไซเคิลองค์ประกอบที่มีเนื้อหาที่อัปเดตคุณเพียงเลือกที่จะไม่แสดงผลอะไรในแผนผังที่ไม่ได้อยู่ในมุมมอง มันเป็นที่ประจักษ์อย่างรวดเร็วใน 5,000 รายการแม้ว่าการใช้งาน POC ที่ง่ายมากของพวกเขาจะทำให้เกิดการสั่นไหวเล็กน้อย ...


นอกจากนี้ ... เพื่อสะท้อนการโพสต์อื่น ๆ การใช้track byจะมีประโยชน์อย่างจริงจังแม้จะมีชุดข้อมูลขนาดเล็ก พิจารณามันบังคับ


ไอเดียสุดยอดโดยทีม Ionic ฉันสงสัยว่าสิ่งนั้นมาจากการแสดงผลของมุมมองดั้งเดิมหรือไม่
แบรดลีย์ท่วม

ตัวอย่างเช่น UITableView ใน iOS ใช้วิธีการเดียวกันในการเรนเดอร์ชุดข้อมูลขนาดใหญ่ ฉันคิดว่านี่เป็นวิธีการทั่วไปที่ใช้ในมุมมองดั้งเดิมหลายประการ
Dmitry Kotenko

36

ฉันขอแนะนำให้ดูสิ่งนี้:

การเพิ่มประสิทธิภาพ AngularJS: 1200ms ถึง 35ms

พวกเขาสร้างคำสั่งใหม่โดยการปรับ 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- แทนที่ 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 Aggarwal

15

นอกเหนือจากคำแนะนำข้างต้นทั้งหมดเช่น track by และ loops ที่เล็กลงอันนี้ก็ช่วยฉันได้มาก

<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

ด้วยการติดตามโดยคุณจะไม่ติดตามองค์ประกอบอาร์เรย์ตั้งแต่ต้นทุกครั้งที่คุณรับข้อมูลใหม่ ด้วยเหตุนี้การปรับปรุงประสิทธิภาพ
user1920302

2
สิ่งนี้มีประโยชน์ก็ต่อเมื่อข้อมูลในการทำซ้ำ ng เปลี่ยนแปลง สำหรับการโหลดครั้งแรกอาจไม่สร้างการปรับปรุงประสิทธิภาพ
Sumesh Kuttan

11

หากแถวทั้งหมดของคุณมีความสูงเท่ากันคุณควรดู virtualizing ng-repeat: 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?
Antonis

9

การเลื่อนเสมือนจริงเป็นอีกวิธีหนึ่งในการปรับปรุงประสิทธิภาพการเลื่อนเมื่อต้องรับมือกับรายการขนาดใหญ่และชุดข้อมูลขนาดใหญ่

วิธีหนึ่งในการดำเนินการนี้คือการใช้วัสดุเชิงมุม md-virtual-repeatซึ่งแสดงให้เห็นในการสาธิตนี้กับ 50,000 รายการ

ถ่ายตรงจากเอกสารการทำซ้ำเสมือนจริง:

การทำซ้ำเสมือนเป็นการทดแทนแบบ จำกัด สำหรับ ng-repeat ที่วาทกรรมโหนด dom เพียงพอที่จะเติมภาชนะ


2
ว้าวฉันคิดว่านี่เป็นคำตอบที่น่าสนใจที่สุด สิ่งนี้จะใช้ได้กับแองกูลาร์เวอร์ชันเก่ากว่าหรือ (เช่น ver 1.2)
Thariq Nugrohotomo

2
@ThariqNugrohotomo โปรดทราบว่าการใช้วัสดุเชิงมุมต้องใช้ 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 และในขณะที่มันทำให้หน้าตอบสนองมากขึ้นในตอนแรกมันก็ตอบสนองน้อยลงหลังจากแต่ละอัน
พอล Brannan

นี่เป็นปัญหาใหญ่ในแอปของฉันหลังจากมีรายการมากกว่า 500 รายการ ฉันแนะนำการให้เลขหน้าหรือโหลดไม่ จำกัด แทน
joalcego

0

บางครั้งสิ่งที่เกิดขึ้นคุณจะได้รับข้อมูลจากเซิร์ฟเวอร์ (หรือแบ็คเอนด์) ในไม่กี่มิลลิวินาที (ตัวอย่างเช่นฉันฉันกำลังสมมติว่ามัน 100ms) แต่มันต้องใช้เวลามากขึ้นในการแสดงผลในหน้าเว็บของเรา (ขอบอกว่ามันคือการ 900ms ไป แสดง).

ดังนั้นสิ่งที่เกิดขึ้นที่นี่คือ 800ms มันเป็นเพียงการแสดงผลหน้าเว็บ

สิ่งที่ฉันทำในเว็บแอปพลิเคชันของฉันคือฉันใช้การแบ่งหน้า (หรือคุณสามารถใช้เลื่อนแบบไม่สิ้นสุด ) เพื่อแสดงรายการข้อมูล สมมติว่าฉันกำลังแสดงข้อมูล 50 หน้า /

ดังนั้นฉันจะไม่โหลดแสดงข้อมูลทั้งหมดในครั้งเดียวมีเพียง 50 ข้อมูลที่ฉันกำลังโหลดในตอนแรกซึ่งใช้เวลาเพียง 50 มิลลิวินาที (ฉันถือว่าที่นี่)

ดังนั้นเวลาทั้งหมดที่นี่ลดลงจาก 900 มิลลิวินาทีเป็น 150 มิลลิวินาทีเมื่อผู้ใช้ร้องขอหน้าถัดไปจากนั้นแสดงข้อมูล 50 อันดับถัดไปและอื่น ๆ

หวังว่าสิ่งนี้จะช่วยคุณในการปรับปรุงประสิทธิภาพ ทั้งหมดที่ดีที่สุด


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;

    }
}

การสาธิตด้วยตาราง UI พร้อมการสาธิตการเลื่อนแบบไม่มีที่สิ้นสุด


ยินดีต้อนรับลิงก์ไปยังโซลูชัน แต่โปรดตรวจสอบให้แน่ใจว่าคำตอบของคุณมีประโยชน์หากไม่มี: เพิ่มบริบทรอบลิงก์เพื่อให้ผู้ใช้เพื่อนของคุณมีความคิดว่ามันคืออะไรและเพราะเหตุใดจึงอยู่ที่นั่น กำลังเชื่อมโยงไปยังในกรณีที่หน้าเป้าหมายไม่พร้อมใช้งาน คำตอบที่มีน้อยกว่าการเชื่อมโยงอาจถูกลบออก
SᴀᴍOnᴇᴌᴀ

-2

สำหรับชุดข้อมูลขนาดใหญ่และวางมูลค่าหลายลงมันจะดีกว่าที่จะใช้มากกว่าng-optionsng-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-repeat -1
Icet

ใช้งานได้กับการเลือกเท่านั้น ng-repeat จะมีประสิทธิภาพยิ่งกว่า อย่างไรก็ตามมันเป็นความจริงที่ ng-Options เร็วกว่า ng-options AngularJs docs พูดถึง 2,000 รายการสำหรับความแตกต่าง: docs.angularjs.org/api/ng/directive/select
kaiser
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.