ข้อผิดพลาด: ถึงการทำซ้ำ 10 $ Digest () แล้ว แท้ง! ด้วยเพรดิเคตการเรียงลำดับแบบไดนามิก


135

ฉันมีรหัสต่อไปนี้ที่ซ้ำและแสดงชื่อผู้ใช้และคะแนนของเขา:

<div ng-controller="AngularCtrl" ng-app>
  <div ng-repeat="user in users | orderBy:predicate:reverse | limitTo:10">
    <div ng-init="user.score=user.id+1">
        {{user.name}} and {{user.score}}
    </div>
  </div>
</div>

และตัวควบคุมเชิงมุมที่สอดคล้องกัน

function AngularCtrl($scope) {
    $scope.predicate = 'score';
    $scope.reverse = true;
    $scope.users = [{id: 1, name: 'John'}, {id: 2, name: 'Ken'}, {id: 3, name: 'smith'}, {id: 4, name: 'kevin'}, {id: 5, name: 'bob'}, {id: 6, name: 'Dev'}, {id: 7, name: 'Joe'}, {id: 8, name: 'kevin'}, {id: 9, name: 'John'}, {id: 10, name: 'Ken'}, {id: 11, name: 'John'}, {id: 1, name: 'John'}, {id: 2, name: 'Ken'}, {id: 3, name: 'smith'}, {id: 4, name: 'kevin'}, {id: 5, name: 'bob'}, {id: 6, name: 'Dev'}, {id: 7, name: 'Joe'}, {id: 8, name: 'kevin'}, {id: 9, name: 'John'}, {id: 10, name: 'Ken'}]
}

เมื่อฉันเรียกใช้โค้ดด้านบนฉันได้รับข้อผิดพลาด: ถึงการทำซ้ำ 10 $ Digest () แท้ง! ข้อผิดพลาดในคอนโซลของฉัน

ฉันได้สร้างjsfiddleสำหรับสิ่งเดียวกัน

เพรดิเคตการเรียงลำดับจะถูกกำหนดค่าเริ่มต้นภายใน ng-repeat เท่านั้นและยังมีการใช้ขีด จำกัด กับจำนวนอ็อบเจ็กต์ ดังนั้นฉันจึงรู้สึกว่าการมีทั้ง sortby และ limitTo watch ร่วมกันเป็นสาเหตุของข้อผิดพลาด

หาก $ scope.reverse เป็นเท็จ (ลำดับคะแนนจากน้อยไปมาก) แสดงว่าไม่มีข้อผิดพลาด

ใครช่วยฉันเข้าใจว่ามีอะไรผิดปกติที่นี่? ขอบคุณมากสำหรับความช่วยเหลือของคุณ


หากคุณลบคำสั่ง if แล้วยังมีข้อผิดพลาดอยู่หรือไม่?
Mathew Berg

ขอบคุณสำหรับคำตอบของคุณ Mathew! ฉันวินิจฉัยปัญหาผิด ปัญหาดูเหมือนจะอยู่ที่ตัวกรอง sortby และ limitTo ฉันได้อัปเดตคำถามด้วย JSFiddle ขอขอบคุณสำหรับความช่วยเหลือของคุณ
Chubby Boy

นี่คือสิ่งเชิงมุม คุณต้องบันทึกการทำงานของคุณและจากนั้นจึงจะจำสถานะได้ ติดตามคำตอบของฉันได้ที่stackoverflow.com/questions/14376879/…
Julio Marins

คำตอบ:


116

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

เท่าที่ฉันเห็นไม่มีปัญหากับรหัสที่คุณโพสต์ ข้อผิดพลาดที่คุณกล่าวถึงมักเกิดขึ้นเมื่อคุณสร้างการวนซ้ำของการเปลี่ยนแปลงในคุณสมบัติ ตัวอย่างเช่นเมื่อคุณเฝ้าดูการเปลี่ยนแปลงของคุณสมบัติบางอย่างแล้วเปลี่ยนค่าของคุณสมบัตินั้นใน Listener:

$scope.$watch('users', function(value) {
  $scope.users = [];
});

ซึ่งจะส่งผลให้เกิดข้อความแสดงข้อผิดพลาด:

ข้อผิดพลาดที่ไม่ถูกจับ: มีการทำซ้ำ 10 $ Digest () แล้ว แท้ง!
ผู้ชมยิงในการทำซ้ำ 5 ครั้งล่าสุด: ...

ตรวจสอบให้แน่ใจว่าโค้ดของคุณไม่มีสถานการณ์เช่นนี้

ปรับปรุง:

นี่คือปัญหาของคุณ:

<div ng-init="user.score=user.id+1"> 

คุณไม่ควรเปลี่ยนออบเจ็กต์ / โมเดลในระหว่างการเรนเดอร์มิฉะนั้นจะบังคับให้มีการเรนเดอร์ใหม่ (และส่งผลให้เกิดการวนซ้ำซึ่งทำให้เกิดการวนซ้ำ'Error: 10 $ Digest () การยกเลิก!' )

หากคุณต้องการอัปเดตโมเดลให้ทำบนคอนโทรลเลอร์หรือบน Directive ห้ามใช้ในมุมมอง เอกสาร angularjs แนะนำที่จะไม่ใช้ng-initว่าเพื่อหลีกเลี่ยงสถานการณ์เหล่านี้:

ใช้คำสั่ง ngInit ในเทมเพลต (สำหรับแอปของเล่น / ตัวอย่างเท่านั้นไม่แนะนำสำหรับแอปพลิเคชันจริง)

นี่คือjsFiddleพร้อมตัวอย่างการทำงาน


1
FYI แทนคุณangular.element(w).bind()สามารถใช้element.bind().
Mark Rajcok

ขอบคุณ bmleite และ Mark มาก ฉันวินิจฉัยปัญหาผิด โปรดดูความคิดเห็นของฉันด้านบน ฉันได้อัปเดตคำถามด้วย JSFiddle ขอขอบคุณสำหรับความช่วยเหลือของคุณ
Chubby Boy

ขอบคุณมาก @bmleite! แค่พยายามเข้าใจมากขึ้น แต่ทำไมถึงเป็นเช่นนั้นถ้า $ scope.reverse = false (ลำดับคะแนนจากน้อยไปมาก) ไม่ก่อให้เกิดข้อผิดพลาด
Chubby Boy

สำหรับการขึ้นและลงคุณควรใช้คำนำหน้า '+' และ '-' ตามลำดับ (เช่น '+ score' และ '-score') ข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ที่นี่
bmleite

1
คำตอบนี้ช่วยแก้ปัญหาของฉันได้ คำอธิบายเป็นสิ่งที่ดีในการอธิบายถึงปัญหา: ผลข้อผิดพลาดการทำซ้ำ $ Digest () เมื่อคุณพยายามแก้ไขคุณสมบัติในเวลาเดียวกันกับที่คุณวนลูป ดังนั้นการเปลี่ยนแปลงคุณสมบัติแต่ละครั้งจะทำให้เกิดการอัปเดตของมุมมอง (UI) และในที่สุดเชิงมุมจะขยายสูงสุดที่ 10 ลูป ตัวอย่าง jsFiddle ทำให้การเปลี่ยนแปลงคุณสมบัติใน Controller การเปลี่ยนคุณสมบัติใน View ผลลัพธ์ในข้อผิดพลาดนี้
mbokil

9

สาเหตุของข้อผิดพลาดนี้สำหรับฉันคือ ...

ng-if="{{myTrustSrc(chat.src)}}"

ในเทมเพลตของฉัน

มันทำให้ฟังก์ชัน myTrustSrc ในคอนโทรลเลอร์ของฉันถูกเรียกแบบวนซ้ำไม่รู้จบ ถ้าฉันลบ ng-if ออกจากบรรทัดนี้แสดงว่าปัญหาได้รับการแก้ไขแล้ว

<iframe ng-if="chat.src" id='chat' name='chat' class='chat' ng-src="{{myTrustSrc(chat.src)}}"></iframe>

ฟังก์ชันนี้ถูกเรียกเพียงไม่กี่ครั้งเมื่อไม่ได้ใช้ ng-if ฉันยังคงสงสัยว่าทำไมฟังก์ชันจึงถูกเรียกใช้มากกว่าหนึ่งครั้งด้วย ng-src?

นี่คือฟังก์ชั่นในคอนโทรลเลอร์

$scope.myTrustSrc = function(src) {
    return $sce.trustAsResourceUrl(src);
}

ฉันยังคงสงสัยว่าทำไมฟังก์ชันจึงถูกเรียกใช้มากกว่าหนึ่งครั้งด้วย ng-src? - เนื่องจาก angular พยายามสร้าง html สองสามครั้งจนกว่าจะได้ผลลัพธ์ 2 รายการที่เหมือนกัน นั่นคือความหมายของการทำซ้ำข้อผิดพลาด 10 $ Digest () ถึงเขาพยายาม 10 ครั้งและไม่เคยได้ผลลัพธ์ที่เหมือนกัน 2 รายการ
bresleveloper

@bresleveloper ช่วยส่งลิงค์ข้อมูลนี้ให้หน่อยได้ไหม ขอบคุณ.
Willa

@Willa ดูที่นี่docs.angularjs.org/api/ng/type/$rootScope.Scope CTRF + F นี้ "ขีด จำกัด การรีรันซ้ำคือ 10 เพื่อป้องกันการหยุดชะงักของลูปที่ไม่สิ้นสุด"
bresleveloper

ขอบคุณ. ที่ช่วย
Willa

7

สำหรับฉันมันคือการที่ฉันส่งผลลัพธ์ของฟังก์ชันเป็นอินพุตการผูก 2 ทาง '=' ไปยังคำสั่งที่สร้างวัตถุใหม่ทุกครั้ง

ดังนั้นฉันจึงมีบางอย่างเช่นนั้น:

<my-dir>
   <div ng-repeat="entity in entities">
      <some-other-dir entity="myDirCtrl.convertToSomeOtherObject(entity)"></some-other-dir>
   </div>
</my-dir>

และวิธีการควบคุมบน my-dir คือ

this.convertToSomeOtherObject(entity) {
   var obj = new Object();
   obj.id = entity.Id;
   obj.value = entity.Value;
   [..]
   return obj;
}

ซึ่งเมื่อฉันทำไป

this.convertToSomeOtherObject(entity) {
   var converted = entity;
   converted.id = entity.Id;
   converted.value = entity.Value;
   [...]
   return converted;
}

แก้ปัญหา!

หวังว่านี่จะช่วยใครสักคน :)


5

ฉันมีอีกตัวอย่างหนึ่งของสิ่งที่ทำให้เกิดสิ่งนี้ หวังว่าจะช่วยสำหรับการอ้างอิงในอนาคต ฉันใช้ AngularJS 1.4.1

ฉันมีมาร์กอัปนี้พร้อมกับการเรียกใช้คำสั่งที่กำหนดเองหลายครั้ง:

<div ng-controller="SomeController">
    <myDirective data="myData.Where('IsOpen',true)"></myDirective>
    <myDirective data="myData.Where('IsOpen',false)"></myDirective>
</div>

myDataเป็นอาร์เรย์และWhere()เป็นวิธีการขยายที่วนซ้ำบนอาร์เรย์ที่ส่งคืนอาร์เรย์ใหม่ที่มีรายการใด ๆ จากเดิมโดยที่คุณสมบัติ IsOpen ตรงกับค่าบูลในพารามิเตอร์ที่สอง

ในคอนโทรลเลอร์ฉันตั้งค่า$scope.dataดังนี้:

DataService.getData().then(function(results){
    $scope.data = results;
});

การเรียกWhere()วิธีการขยายจากคำสั่งเหมือนในมาร์กอัปข้างต้นเป็นปัญหา ในการแก้ไขปัญหานี้ฉันได้ย้ายการโทรไปยังเมธอดส่วนขยายลงในคอนโทรลเลอร์แทนมาร์กอัป:

<div ng-controller="SomeController">
    <myDirective data="openData"></myDirective>
    <myDirective data="closedData"></myDirective>
</div>

และรหัสคอนโทรลเลอร์ใหม่:

DataService.getData().then(function(results){
    $scope.openData = results.Where('IsOpen',true);
    $scope.closedData = results.Where('IsOpen',false);
});

ฟังดูเหมือนปัญหาที่ฉันมี ฟังก์ชันง่ายๆที่ไม่ได้อัปเดตอะไรเลย แต่สร้างอาร์เรย์โลคัลโดยวนซ้ำบนอาร์เรย์ $ ขอบเขตหนึ่งที่มีอยู่และสร้างอ็อบเจ็กต์ในอาร์เรย์ภายในเครื่องและส่งคืน การโทรครั้งเดียวและเก็บไว้ในสมาชิกขอบเขต $ จะช่วยแก้ปัญหาได้ ขอบคุณ. หวังว่าฉันจะรู้ว่าปัญหาในการใช้วิธีนี้โดยตรงคืออะไร
AgilePro

2

ค่อนข้างช้าไปงานปาร์ตี้ แต่ปัญหาของฉันเกิดขึ้นเนื่องจากมีข้อบกพร่องใน ui-router ในเชิงมุม 1.5.8 สิ่งที่ต้องพูดถึงก็คือข้อผิดพลาดนี้เกิดขึ้นในครั้งแรกที่ฉันเรียกใช้แอปพลิเคชันเท่านั้นและจะไม่เกิดขึ้นอีกในภายหลัง โพสต์จาก github นี้ช่วยแก้ปัญหาของฉันได้ โดยทั่วไปแล้วข้อผิดพลาดเกี่ยวข้องกับ$urlRouterProvider.otherwise("/home") การแก้ปัญหาเป็นวิธีแก้ปัญหาดังนี้:

$urlRouterProvider.otherwise(function($injector, $location) {
   var $state = $injector.get("$state");
   $state.go("your-state-for-home");
});

2

สำหรับผู้เริ่มไม่สนใจคำตอบทั้งหมดพร้อมบอกให้คุณใช้ $ watch Angular ใช้งานได้จากผู้ฟังอยู่แล้ว ฉันรับประกันว่าคุณกำลังทำให้สิ่งต่าง ๆ ซับซ้อนโดยเพียงแค่คิดในทิศทางนี้

ละเว้นคำตอบทั้งหมดที่บอกให้คุณใช้ $ timeout คุณไม่สามารถทราบได้ว่าจะต้องใช้เวลานานเท่าใดในการโหลดหน้านี้จึงไม่ใช่วิธีที่ดีที่สุด

คุณจะต้องทราบเมื่อแสดงผลเพจเสร็จแล้วเท่านั้น

<div ng-app='myApp'>
<div ng-controller="testctrl">
    <label>{{total}}</label>
    <table>
      <tr ng-repeat="item in items track by $index;" ng-init="end($index);">
        <td>{{item.number}}</td>
      </tr>
    </table>
</div>

var app = angular.module('myApp', ["testctrl"]);
var controllers = angular.module("testctrl", []);
 controllers.controller("testctrl", function($scope) {

  $scope.items = [{"number":"one"},{"number":"two"},{"number":"three"}];

  $scope.end = function(index){
  if(index == $scope.items.length -1
        && typeof $scope.endThis == 'undefined'){

            ///  DO STUFF HERE
      $scope.total = index + 1;
      $scop.endThis  = true;
 }
}
});

ติดตาม ng-repeat by $ index และเมื่อความยาวของอาร์เรย์เท่ากับดัชนีหยุดลูปและทำตรรกะของคุณ

jsfiddle


1

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


1

มันแปลก ... ฉันมีข้อผิดพลาดเหมือนกันทุกประการมาจากสิ่งอื่น เมื่อฉันสร้างคอนโทรลเลอร์ของฉันฉันส่งผ่านพารามิเตอร์ $ location ดังนี้:

App.controller('MessageController', function ($scope, $http, $log, $location, $attrs, MessageFactory, SocialMessageFactory) {
   // controller code
});

สิ่งนี้ได้รับการพิสูจน์แล้วว่าเป็นจุดบกพร่อง เมื่อเราใช้ไลบรารีของบุคคลที่สามหรือ JS บริสุทธิ์เพื่อจัดการกับข้อมูลเฉพาะบางอย่าง (ที่นี่ window.location) การย่อยเชิงมุมถัดไปจะทำให้ข้อผิดพลาดนี้เกิดขึ้น

ดังนั้นฉันจึงเพียงแค่ลบ$ locationออกจากพารามิเตอร์การสร้างคอนโทรลเลอร์และมันก็ใช้งานได้อีกครั้งโดยไม่มีข้อผิดพลาดนี้ หรือถ้าคุณอย่างต้องใช้ตำแหน่ง $ จากมุมที่คุณต้องเอาทุกเดียว<a href="#">link</a>ในการเชื่อมโยงของหน้าของคุณแม่และค่อนข้างเขียนhref = "" ทำงานให้ฉัน

หวังว่ามันจะช่วยได้สักวัน



0

สิ่งนี้เกิดขึ้นกับฉันทันทีหลังจากอัปเกรด Firefox เป็นเวอร์ชัน 51 หลังจากนั้นclearing the cacheปัญหาก็หายไป



0

สิ่งนี้เกิดขึ้นกับฉันหลังจากอัปเกรดจาก angular 1.6 -> 1.7 เมื่อใช้ $ sce.trustAsResourceUrl () เป็นค่าส่งคืนของฟังก์ชันที่เรียกจาก ng-src คุณสามารถมองเห็นปัญหานี้กล่าวถึงที่นี่

ในกรณีของฉันฉันต้องเปลี่ยนสิ่งต่อไปนี้

<source ng-src="{{trustSrc(someUrl)}}" type='video/mp4' />
trustSrc = function(url){
    return $sce.trustAsResourceUrl(url);
};

ถึง

<source ng-src='{{trustedUrl}} type='video/mp4' />
trustedUrl = $sce.trustAsResourceUrl(someUrl);

0

ฉันได้รับข้อผิดพลาดเดียวกันโดยใช้ AngularJS 1.3.9 เมื่อฉันในตัวกรองการเรียงลำดับที่กำหนดเองเรียกใช้Array.reverse ()

หลังจากที่ฉันถอดมันออกแล้วมันก็ทำได้ดี

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