Làm thế nào để lặp lại các mục được trả về bởi một hàm với ng-repeat?


114

Tôi muốn tạo div nhiều lần, các mục là các đối tượng được trả về bởi một hàm. Tuy nhiên, các lỗi báo cáo mã sau: Đã đạt đến 10 lần lặp lại $ tiêu hóa (). Hủy bỏ! jsfiddle ở đây: http://jsfiddle.net/BraveOstrich/awnqm/

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

Câu trả lời:


195

Câu trả lời ngắn gọn : bạn thực sự cần chức năng như vậy hay bạn có thể sử dụng tài sản? http://jsfiddle.net/awnqm/1/

Câu trả lời dài

Để đơn giản, tôi sẽ chỉ mô tả trường hợp của bạn - ngRepeat cho mảng đối tượng. Ngoài ra, tôi sẽ bỏ qua một số chi tiết.

AngularJS sử dụng kiểm tra bẩn để phát hiện các thay đổi. Khi ứng dụng được bắt đầu nó chạy $digestcho $rootScope. $digestsẽ thực hiện chuyển tải theo chiều sâu cho hệ thống phân cấp của phạm vi . Tất cả các phạm vi đều có danh sách đồng hồ. Mỗi đồng hồ có giá trị cuối cùng (ban đầu initWatchVal). Đối với mỗi phạm vi cho tất cả các đồng hồ $digestchạy nó, nhận giá trị hiện tại ( watch.get(scope)) và so sánh với watch.last. Nếu giá trị hiện tại không bằng watch.last(luôn cho lần so sánh đầu tiên) $digestđặt dirtythành true. Khi tất cả các phạm vi được xử lý nếu dirty == true $digestbắt đầu một chuyển tải theo chiều sâu khác từ đó $rootScope. $digestkết thúc khi dơ bẩn == false hoặc số lượng truyền qua == 10. Trong trường hợp thứ hai, lỗi "đã đạt đến 10 lần lặp lại hàm tiêu hóa ()." sẽ được ghi lại.

Bây giờ về ngRepeat. Đối với mỗi watch.getlệnh gọi, nó lưu trữ các đối tượng từ bộ sưu tập (giá trị trả về của getEntities) với thông tin bổ sung trong bộ nhớ cache ( HashQueueMapbằng hashKey). Đối với mọi watch.getcuộc gọi ngRepeatcố gắng lấy đối tượng của nó hashKeytừ bộ nhớ cache. Nếu nó không tồn tại trong bộ nhớ cache, ngRepeatlưu trữ nó trong bộ nhớ cache, tạo ra phạm vi mới, puts đối tượng vào nó, tạo ra phần tử DOM, vv .

Bây giờ về hashKey. Thường hashKeylà số duy nhất được tạo bởi nextUid(). Nhưng nó có thể hoạt động . hashKeyđược lưu trữ trong đối tượng sau khi tạo để sử dụng trong tương lai.

Tại sao ví dụ của bạn tạo ra lỗi : hàm getEntities()luôn trả về mảng với đối tượng mới. Đối tượng này không có hashKeyvà không tồn tại trong ngRepeatbộ nhớ cache. Vì vậy, ngRepeattrên mỗi lần watch.gettạo phạm vi mới cho nó với đồng hồ mới cho {{entity.id}}. Đồng hồ này đầu tiên watch.getwatch.last == initWatchVal. Vì vậy watch.get() != watch.last. Vì vậy, $digestbắt đầu hành trình mới. Vì vậy, hãy ngRepeattạo phạm vi mới với đồng hồ mới. Vì vậy, ... sau 10 lần duyệt bạn sẽ gặp lỗi.

Làm thế nào bạn có thể sửa chữa nó

  1. Không tạo đối tượng mới trong mỗi getEntities()cuộc gọi.
  2. Nếu bạn cần tạo các đối tượng mới, bạn có thể thêm hashKeyphương thức cho chúng. Xem chủ đề này để biết ví dụ.

Hy vọng những người biết AngularJS nội bộ sẽ sửa cho tôi nếu tôi sai ở một số điểm.


4
+1 cảm ơn bạn vì điều này. Tôi đã gặp vấn đề tương tự và không thể sử dụng thuộc tính tĩnh cho nó. $$ hashKey thực sự phải được ghi lại trên trang ngRepeat của IMO thủ công.
Michael Moussa

Bất kỳ ý tưởng nào đã thay đổi từ 1.1.3 thành 1.1.4 ảnh hưởng đến điều này? Trước 1.1.4, điều này thực sự hoạt động. Không có gì trong bảng thay đổi về nó và tôi không thể giải thích sự khác biệt là gì. Hành vi hiện tại có ý nghĩa.
m59

Ngoài ra, hãy kiểm tra điều này nếu bạn có thể: stackoverflow.com/questions/20933261/… Tôi không chắc liệu câu trả lời của mình có phải là con đường để đi hay không ..
m59 5/114

2
Vì vậy, việc làm theo khuyến nghị đối với Do not create new objects on every getEntities() call.nó có thể được khắc phục khá dễ dàng như thế này:<div ng-repeat="entity in entities = (entities || getEntities())">
przno

2
giải pháp từ nhận xét trước đây của tôi hoạt động trong trường hợp getEntities()luôn trả về cùng một mảng, nếu mảng thay đổi, bạn sẽ không nhận được nó trongng-repeat
przno

44

Khởi tạo mảng bên ngoài lặp lại

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

8
Điều này không hoạt động nếu getEntities()trả về một cái gì đó khác nhau trong vòng đời của chương trình. Ví dụ, giả sử getEntities()kích hoạt một $http.get. Khi sự việc cuối cùng được giải quyết (bạn đã thực hiện cuộc gọi AJAX), entitiessẽ được khởi tạo.
Nighto

3
Từ các tài liệu góc cạnhThe 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

Tôi nghĩ điểm chính là "khởi tạo mảng bên ngoài lặp lại" bằng bất cứ cách nào ... và @Nighto kêu gọi tốt về những lời hứa
Mwayi

15

Điều này đã được báo cáo ở đây và nhận được phản hồi này:

Getter của bạn không phải là idmpotent và thay đổi mô hình (bằng cách tạo một mảng mới mỗi khi nó được gọi). Điều này buộc góc tiếp tục gọi nó với hy vọng rằng mô hình cuối cùng sẽ ổn định, nhưng nó không bao giờ vì vậy góc từ bỏ và ném ra một ngoại lệ.

Các giá trị mà getter trả về là bằng nhau nhưng không giống nhau và đó là vấn đề.

Bạn có thể thấy hiện tượng này biến mất nếu bạn di chuyển mảng ra ngoài Bộ điều khiển chính:

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

bởi vì bây giờ nó đang trả về cùng một đối tượng mỗi lần. Bạn có thể cần phải kiến ​​trúc lại mô hình của mình để sử dụng một thuộc tính trên phạm vi thay vì một hàm:

Chúng tôi đã giải quyết vấn đề đó bằng cách gán kết quả của phương thức của bộ điều khiển cho một thuộc tính và thực hiện ng: lặp lại với nó.


Sử dụng một thuộc tính có thể là cách duy nhất nếu hàm có một tham số thay đổi trên mỗi lần lặp.
Stephane

7

Dựa trên bình luận @przno

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

Giải pháp thứ hai của BTW @Artem Andreev đề xuất không hoạt động trong Angular 1.1.4 trở lên, trong khi giải pháp đầu tiên không giải quyết được vấn đề. Vì vậy, tôi e rằng hiện tại đây là giải pháp ít phức tạp hơn mà không có nhược điểm về chức năng


Ý bạn là item.id? Nếu ý bạn là entity.id, bạn có thể vui lòng giải thích không? Cảm ơn nhiều!
Gerfried

Vâng, bạn đúng. Item.idlà những gì nó nên b. Cảm ơn
Agat
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.