วิธีการวนซ้ำรายการที่ส่งคืนโดยฟังก์ชันด้วย ng-repeat?


114

ฉันต้องการสร้าง div ซ้ำ ๆ รายการคือวัตถุที่ส่งคืนโดยฟังก์ชัน อย่างไรก็ตามข้อผิดพลาดในการรายงานรหัสต่อไปนี้: ถึงการทำซ้ำ 10 $ Digest () ยกเลิก! jsfiddle อยู่ที่นี่: http://jsfiddle.net/BraveOstrich/awnqm/

<body ng-app>
  <div ng-repeat="entity in getEntities()">
    Hello {{entity.id}}!
  </div>
</body>

คำตอบ:


195

คำตอบสั้น ๆ : คุณต้องการฟังก์ชั่นดังกล่าวจริงๆหรือคุณสามารถใช้คุณสมบัติได้? http://jsfiddle.net/awnqm/1/

คำตอบยาว

เพื่อความง่ายฉันจะอธิบายเฉพาะกรณีของคุณ - ngRepeat สำหรับอาร์เรย์ของวัตถุ นอกจากนี้ฉันจะไม่ใส่รายละเอียดบางอย่าง

AngularJS ใช้การตรวจสอบสกปรกเพื่อตรวจจับการเปลี่ยนแปลง เมื่อโปรแกรมประยุกต์จะเริ่มต้นมันจะทำงานสำหรับ$digest จะทำข้ามความลึกเป็นครั้งแรกสำหรับลำดับชั้นขอบเขตของ ขอบเขตทั้งหมดมีรายการนาฬิกา นาฬิกาแต่ละเรือนมีค่าสุดท้าย (เริ่มแรก) สำหรับขอบเขตสำหรับนาฬิกาทุกแต่ละมันวิ่งได้รับค่าปัจจุบัน ( ) และเปรียบเทียบกับ ถ้าค่าปัจจุบันไม่เท่ากับ(เสมอแรกเปรียบเทียบ) ชุดที่จะ เมื่อขอบเขตทั้งหมดจะถูกประมวลผลถ้าจะเริ่มต้นอีก traversal ลึกแรกจาก สิ้นสุดเมื่อสกปรก == เท็จหรือจำนวนการข้ามผ่าน == 10 ในกรณีหลังข้อผิดพลาด "การทำซ้ำ 10 $ Digest () ถึงแล้ว" จะถูกบันทึก$rootScope$digestinitWatchVal$digestwatch.get(scope)watch.lastwatch.last$digestdirtytruedirty == true $digest$rootScope$digest

ตอนนี้เกี่ยวกับngRepeat. สำหรับการwatch.getเรียกแต่ละครั้งจะเก็บอ็อบเจ็กต์จากคอลเล็กชัน (ส่งคืนค่าของgetEntities) พร้อมข้อมูลเพิ่มเติมในแคช ( HashQueueMapโดยhashKey) สำหรับการwatch.getโทรทุกครั้งngRepeatพยายามรับวัตถุhashKeyจากแคช ถ้ามันไม่ได้อยู่ในแคชngRepeatเก็บไว้ในแคชสร้างขอบเขตใหม่ทำให้วัตถุที่มันสร้างองค์ประกอบ DOM, ฯลฯ

ตอนนี้เกี่ยวกับhashKey. มักจะเป็นหมายเลขที่ไม่ซ้ำกันสร้างขึ้นโดยhashKey nextUid()แต่ก็สามารถใช้งานได้ hashKeyถูกเก็บไว้ในวัตถุหลังจากสร้างขึ้นเพื่อใช้ในอนาคต

เหตุใดตัวอย่างของคุณจึงสร้างข้อผิดพลาด : ฟังก์ชันgetEntities()จะส่งคืนอาร์เรย์ด้วยวัตถุใหม่เสมอ วัตถุนี้ไม่มีhashKeyและไม่มีอยู่ในngRepeatแคช ดังนั้นngRepeatในแต่ละสร้างขอบเขตใหม่ได้กับนาฬิกาใหม่สำหรับwatch.get {{entity.id}}นาฬิกานี้ในครั้งแรกwatch.getมีwatch.last == initWatchVal. watch.get() != watch.lastดังนั้น ดังนั้น$digestเริ่มการสำรวจใหม่ ดังนั้นngRepeatสร้างขอบเขตใหม่ด้วยนาฬิกาใหม่ ดังนั้น ... หลังจากผ่านไป 10 ครั้งคุณจะได้รับข้อผิดพลาด

คุณจะแก้ไขได้อย่างไร

  1. อย่าสร้างวัตถุใหม่ทุกครั้งที่getEntities()โทร
  2. หากคุณต้องการสร้างวัตถุใหม่คุณสามารถเพิ่มhashKeyวิธีการสำหรับวัตถุเหล่านั้นได้ ดูหัวข้อนี้สำหรับตัวอย่าง

หวังว่าคนที่รู้ภายใน AngularJS จะแก้ไขฉันได้ถ้าฉันทำอะไรผิด


4
+1 ขอบคุณสำหรับสิ่งนี้ ฉันมีปัญหาเดียวกันและไม่สามารถใช้คุณสมบัติคงที่ได้ $$ hashKey ควรได้รับการบันทึกไว้ในหน้า ngRepeat ของ IMO ด้วยตนเอง
Michael Moussa

มีความคิดอะไรบ้างที่เปลี่ยนจาก 1.1.3 เป็น 1.1.4 ที่ส่งผลต่อสิ่งนี้ ก่อน 1.1.4 นี้ใช้งานได้จริง ไม่มีอะไรในบันทึกการเปลี่ยนแปลงเกี่ยวกับเรื่องนี้และฉันไม่สามารถหาเหตุผลได้ว่าความแตกต่างคืออะไร พฤติกรรมปัจจุบันเข้าท่า
m59

นอกจากนี้ลองดูว่าคุณทำได้ไหม: stackoverflow.com/questions/20933261/…ฉันไม่แน่ใจว่าคำตอบของฉันคือทางไปหรือไม่ ..
m59

2
ดังนั้นการปฏิบัติตามคำแนะนำDo not create new objects on every getEntities() call.จึงสามารถแก้ไขได้อย่างง่ายดายเช่นนี้<div ng-repeat="entity in entities = (entities || getEntities())">
przno

2
วิธีแก้ปัญหาจากความคิดเห็นก่อนหน้าของฉันใช้งานได้ในกรณีที่getEntities()ส่งคืนอาร์เรย์เดียวกันเสมอหากอาร์เรย์เปลี่ยนไปคุณจะไม่ได้รับในng-repeat
przno

44

เริ่มต้นอาร์เรย์ภายนอกการทำซ้ำ

<body ng-app>
   <div ng-init="entities = getEntities()">
       <div ng-repeat="entity in entities">
           Hello {{entity.id}}!
       </div>
   </div>
</body>

8
สิ่งนี้จะไม่ได้ผลหากgetEntities()ส่งคืนสิ่งที่แตกต่างออกไปในวงจรชีวิตของโปรแกรม ตัวอย่างเช่นพูดว่าgetEntities()ทริกเกอร์$http.getไฟล์. เมื่อได้รับการแก้ไขในที่สุด (คุณทำการโทร AJAX) entitiesจะเริ่มต้นแล้ว
Nighto

3
จากเอกสารเชิงมุมThe only appropriate use of ngInit is for aliasing special properties of ngRepeat. Besides this case, you should use controllers rather than ngInit to initialize values on a scope.
Blowsie

ฉันคิดว่าประเด็นหลักคือการ "เริ่มต้นอาร์เรย์นอกการทำซ้ำ" ไม่ว่าจะด้วยวิธีใดก็ตาม ... และ @ คืนคำสัญญาที่ดี
Mwayi

15

สิ่งนี้ได้รับการรายงานที่นี่และได้รับคำตอบนี้:

getter ของคุณไม่ใช่ idempotent และเปลี่ยนโมเดล (โดยการสร้างอาร์เรย์ใหม่ทุกครั้งที่เรียก) นี่คือการบังคับให้เชิงมุมเรียกมันต่อไปโดยหวังว่าโมเดลจะคงตัวในที่สุด แต่มันไม่เคยทำให้เชิงมุมยอมแพ้และโยนข้อยกเว้น

ค่าที่ได้รับกลับมาเท่ากัน แต่ไม่เหมือนกันและนั่นคือปัญหา

คุณสามารถเห็นพฤติกรรมนี้หายไปหากคุณย้ายอาร์เรย์ออกนอกตัวควบคุมหลัก:

var array = [{id:'angularjs'}];
function Main($scope) {
    $scope.getEntities = function(){return array;};
};

เพราะตอนนี้มันส่งคืนวัตถุเดียวกันทุกครั้ง คุณอาจต้องออกแบบโมเดลของคุณใหม่เพื่อใช้คุณสมบัติในขอบเขตแทนฟังก์ชัน:

เราแก้ไขมันโดยกำหนดผลลัพธ์ของวิธีการของคอนโทรลเลอร์ให้กับคุณสมบัติและทำ ng: ทำซ้ำกับมัน


การใช้คุณสมบัติอาจเป็นวิธีเดียวในกรณีที่ฟังก์ชันมีการเปลี่ยนแปลงพารามิเตอร์ในการทำซ้ำแต่ละครั้ง
Stephane

7

อ้างอิงจากความคิดเห็นของ @przno

<body ng-app>
  <div ng-repeat="item in t = angular.equals(t, getEntities()) ? t : getEntities()">
    Hello {{item.id}}!
  </div>
</body>

โซลูชันที่สองของ BTW ที่ @Artem Andreev แนะนำไม่ทำงานใน Angular 1.1.4 ขึ้นไปในขณะที่วิธีแรกไม่สามารถแก้ปัญหาได้ ดังนั้นตอนนี้ฉันกลัวว่านี่เป็นวิธีแก้ปัญหาที่แหลมคมน้อยลงโดยไม่มีข้อเสียในการทำงาน


คุณหมายถึง item.id? ถ้า entity.id คือสิ่งที่คุณหมายถึงคุณช่วยอธิบายได้ไหม ขอบคุณมาก!
Gerfried

ใช่คุณถูก. Item.idคือสิ่งที่ควรข. ขอบคุณ
Agat
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.