Làm thế nào để liên kết dữ liệu hoạt động trong AngularJS?


1957

Làm thế nào để ràng buộc dữ liệu làm việc trong AngularJSkhuôn khổ?

Tôi đã không tìm thấy chi tiết kỹ thuật trên trang web của họ . Rõ ràng là nó hoạt động như thế nào khi dữ liệu được truyền từ khung nhìn sang mô hình. Nhưng làm thế nào để AngularJS theo dõi các thay đổi của các thuộc tính mô hình mà không có setters và getters?

Tôi thấy rằng có những người theo dõi JavaScript có thể làm công việc này. Nhưng chúng không được hỗ trợ trong Internet Explorer 6Internet Explorer 7 . Vậy làm thế nào để AngularJS biết rằng tôi đã thay đổi ví dụ như sau và phản ánh sự thay đổi này trên một khung nhìn?

myobject.myproperty="new value";

10
Xin lưu ý rằng vì angular 1.0.0rc1, bạn cần chỉ định ng-model-Instant ( docs-next.angularjs.org/api/iêu ) để moder của bạn được cập nhật liên tục. Nếu không, nó sẽ được cập nhật vào sự kiện mờ.
Sotomajor

8
Liên kết của Marcello rõ ràng đã bị hỏng, vì vậy đây là một lần nữa: github.com/mhevery/angular.js/blob/master/docs/content/guide/ Lỗi
riffraff

6
@orian, liên kết đó là xấu. được cập nhật thành (tôi giả sử) là như nhau - docs.angularjs.org/guide/databinding
Kevin Meredith

11
Đối với những người vẫn đang đọc câu hỏi này, xin lưu ý rằng Angular 2.0 đã thay đổi rất nhiều cách họ đi về cơ sở dữ liệu kể từ Angular 1.x để làm việc với các thành phần web và giải quyết rất nhiều vấn đề trong các câu trả lời dưới đây.
Tháng Tám

Câu trả lời:


2745

AngularJS ghi nhớ giá trị và so sánh nó với giá trị trước đó. Đây là kiểm tra bẩn cơ bản. Nếu có sự thay đổi về giá trị, thì nó sẽ kích hoạt sự kiện thay đổi.

Các $apply()phương pháp, đó là những gì bạn gọi khi bạn đang chuyển từ một thế giới phi AngularJS vào một thế giới AngularJS, gọi điện $digest(). Một thông báo chỉ đơn giản là kiểm tra bẩn cũ. Nó hoạt động trên tất cả các trình duyệt và hoàn toàn có thể dự đoán được.

Để đối chiếu kiểm tra bẩn (AngularJS) so với người nghe thay đổi ( KnockoutJSBackbone.js ): Mặc dù việc kiểm tra bẩn có vẻ đơn giản và thậm chí không hiệu quả (tôi sẽ giải quyết vấn đề đó sau), hóa ra nó luôn đúng về mặt ngữ nghĩa, trong khi người nghe thay đổi có rất nhiều trường hợp góc kỳ lạ và cần những thứ như theo dõi phụ thuộc để làm cho nó đúng hơn về mặt ngữ nghĩa. Theo dõi phụ thuộc KnockoutJS là một tính năng thông minh cho một vấn đề mà AngularJS không có.

Các vấn đề với người nghe thay đổi:

  • Cú pháp rất tàn bạo, vì các trình duyệt không hỗ trợ nó nguyên bản. Vâng, có proxy, nhưng chúng không đúng về mặt ngữ nghĩa trong mọi trường hợp, và tất nhiên không có proxy trên các trình duyệt cũ. Điểm mấu chốt là kiểm tra bẩn cho phép bạn thực hiện POJO , trong khi KnockoutJS và Backbone.js buộc bạn phải kế thừa từ các lớp của họ và truy cập dữ liệu của bạn thông qua các trình truy cập.
  • Thay đổi liên kết. Giả sử bạn có một mảng các mặt hàng. Giả sử bạn muốn thêm các mục vào một mảng, như bạn đang lặp để thêm, mỗi lần bạn thêm bạn đang thực hiện các sự kiện thay đổi, đó là kết xuất UI. Điều này là rất xấu cho hiệu suất. Những gì bạn muốn là chỉ cập nhật giao diện người dùng một lần, vào cuối. Các sự kiện thay đổi là quá tốt.
  • Người nghe thay đổi kích hoạt ngay lập tức trên một setter, đây là một vấn đề, vì người nghe thay đổi có thể thay đổi dữ liệu hơn nữa, điều này tạo ra nhiều sự kiện thay đổi hơn. Điều này là xấu vì trên ngăn xếp của bạn, bạn có thể có một số sự kiện thay đổi xảy ra cùng một lúc. Giả sử bạn có hai mảng cần được giữ đồng bộ vì bất kỳ lý do gì. Bạn chỉ có thể thêm cái này hoặc cái kia, nhưng mỗi lần bạn thêm bạn sẽ kích hoạt một sự kiện thay đổi, giờ đây có một cái nhìn không nhất quán về thế giới. Đây là một vấn đề rất giống với khóa luồng, mà JavaScript tránh được vì mỗi cuộc gọi lại thực hiện riêng và hoàn thành. Thay đổi sự kiện phá vỡ điều này vì setters có thể có hậu quả sâu rộng mà không có ý định và không rõ ràng, điều này tạo ra vấn đề chủ đề một lần nữa. Nó chỉ ra rằng những gì bạn muốn làm là trì hoãn việc thực hiện người nghe và đảm bảo,

Hiệu suất thì sao?

Vì vậy, có vẻ như chúng ta chậm, vì kiểm tra bẩn là không hiệu quả. Đây là nơi chúng ta cần xem xét các số thực thay vì chỉ có các đối số lý thuyết, nhưng trước tiên hãy xác định một số ràng buộc.

Con người là:

  • Chậm - Bất cứ điều gì nhanh hơn 50 ms là không thể chấp nhận được đối với con người và do đó có thể được coi là "tức thời".

  • Hạn chế - Bạn thực sự không thể hiển thị hơn 2000 mẩu thông tin cho một người trên một trang. Bất cứ điều gì hơn thế là UI thực sự xấu và con người không thể xử lý việc này.

Vì vậy, câu hỏi thực sự là đây: Bạn có thể thực hiện bao nhiêu so sánh trên trình duyệt trong 50 ms? Đây là một câu hỏi khó trả lời khi có nhiều yếu tố xuất hiện, nhưng đây là một trường hợp thử nghiệm: http://jsperf.com/angularjs-digest/6 tạo ra 10.000 người theo dõi. Trên một trình duyệt hiện đại, việc này chỉ mất dưới 6 ms. Trên Internet Explorer 8 , mất khoảng 40 ms. Như bạn có thể thấy, đây không phải là vấn đề ngay cả trên các trình duyệt chậm hiện nay. Có một lưu ý: Việc so sánh cần phải đơn giản để phù hợp với giới hạn thời gian ... Thật không may là quá dễ dàng để thêm một so sánh chậm vào AngularJS, vì vậy thật dễ dàng để xây dựng các ứng dụng chậm khi bạn không biết bạn là gì đang làm. Nhưng chúng tôi hy vọng sẽ có câu trả lời bằng cách cung cấp một mô-đun thiết bị, trong đó sẽ cho bạn thấy đó là những so sánh chậm.

Nó chỉ ra rằng các trò chơi video và GPU sử dụng phương pháp kiểm tra bẩn, đặc biệt là vì nó phù hợp. Miễn là chúng vượt qua tốc độ làm mới màn hình (thường là 50-60 Hz, hoặc cứ sau 16,6-20 ms), bất kỳ hiệu suất nào cũng là một sự lãng phí, vì vậy, bạn nên vẽ nhiều thứ hơn, hơn là tăng FPS.


32
@Mark - vâng, trong KO, bạn chỉ cần thêm .extend ({thr ga: 500}) để đợi 500 mili giây sau sự kiện thay đổi cuối cùng trước khi hành động.
Daniel Earwicker

158
Toàn bộ câu trả lời này là tuyệt vời ngoài "Miễn là họ đạt được 50 khung hình / giây, bất kỳ hiệu suất nào cũng là một sự lãng phí, vì mắt người không thể đánh giá cao nó, vì vậy, bạn nên vẽ nhiều thứ hơn, hơn là lấy khung hình / giây cao hơn." Tuyên bố đó là hoàn toàn không chính xác tùy thuộc vào ứng dụng của bạn. Mắt chắc chắn có thể đánh giá cao hơn 50 khung hình / giây và vì các vấn đề khác nhau với chương trình VR (đọc bất kỳ thông tin mới nhất nào từ John Carmack hoặc Michael Abrash, đặc biệt là cuộc nói chuyện GDC 2013 VR sau này), 50 khung hình / giây thực sự quá chậm. Ngoài ra, câu trả lời của bạn là tuyệt vời. Tôi chỉ không muốn thông tin sai lệch lan rộng.
Nate Bundy

10
@DavidRivers chúng tôi là những người giống như trong utorrent 1 Sự thay đổi =
0,000001s

33
Tuyên bố có thể dễ dàng nói ngược lại vì "Kiểm tra bẩn là một tính năng thông minh cho một vấn đề mà loại trực tiếp không có". ES6 đang sử dụng các thiết bị quan sát và góc cạnh đang thoát khỏi việc kiểm tra bẩn. Thế giới thực bắt kịp câu trả lời này và cho thấy nó là sai.
hình nón

17
"Bất cứ điều gì nhanh hơn 50 ms là không thể chấp nhận được đối với con người" là không đúng. Trong thử nghiệm, chúng tôi đã tìm thấy khách hàng của mình có thể dễ dàng phân biệt giữa độ trễ cập nhật 50ms (20fps) và độ trễ cập nhật 16.6ms (60fps). Các cảnh chạy ở tốc độ trước đây luôn bị xếp hạng "cảm giác như thế nào" kém hơn ngay cả khi mọi người không có ý thức đăng ký tốc độ khung hình.
Crashworks

323

Misko đã đưa ra một mô tả tuyệt vời về cách thức liên kết dữ liệu hoạt động, nhưng tôi muốn thêm quan điểm của tôi về vấn đề hiệu suất với ràng buộc dữ liệu.

Như Misko đã nêu, khoảng 2000 ràng buộc là nơi bạn bắt đầu thấy vấn đề, nhưng dù sao bạn cũng không nên có hơn 2000 mẩu thông tin trên một trang. Điều này có thể đúng, nhưng không phải mọi ràng buộc dữ liệu đều hiển thị cho người dùng. Khi bạn bắt đầu xây dựng bất kỳ loại tiện ích hoặc lưới dữ liệu nào với ràng buộc hai chiều, bạn có thể dễ dàng đạt được 2000 ràng buộc mà không gặp phải UX xấu.

Ví dụ, hãy xem xét một hộp tổ hợp nơi bạn có thể nhập văn bản để lọc các tùy chọn có sẵn. Loại điều khiển này có thể có ~ 150 mặt hàng và vẫn có khả năng sử dụng cao. Nếu nó có một số tính năng bổ sung (ví dụ một lớp cụ thể trên tùy chọn hiện được chọn), bạn bắt đầu nhận được 3-5 ràng buộc cho mỗi tùy chọn. Đặt ba trong số các vật dụng này trên một trang (ví dụ: một để chọn một quốc gia, còn lại để chọn một thành phố ở quốc gia nói trên và thứ ba để chọn một khách sạn) và bạn đã ở khoảng 1000 đến 2000 ràng buộc.

Hoặc xem xét một lưới dữ liệu trong một ứng dụng web của công ty. 50 hàng trên mỗi trang không phải là không hợp lý, mỗi hàng có thể có 10-20 cột. Nếu bạn xây dựng điều này với ng-lặp lại và / hoặc có thông tin trong một số ô sử dụng một số ràng buộc, bạn có thể tiếp cận 2000 liên kết chỉ với lưới này.

Tôi thấy đây là một vấn đề lớn khi làm việc với AngularJS và giải pháp duy nhất tôi có thể tìm thấy cho đến nay là xây dựng các widget mà không sử dụng ràng buộc hai chiều, thay vì sử dụng ngOnce, hủy đăng ký theo dõi và các thủ thuật tương tự hoặc xây dựng các lệnh xây dựng DOM với thao tác jQuery và DOM. Tôi cảm thấy điều này đánh bại mục đích sử dụng Angular ngay từ đầu.

Tôi rất thích nghe đề xuất về các cách khác để xử lý việc này, nhưng sau đó có lẽ tôi nên viết câu hỏi của riêng mình. Tôi muốn đưa điều này vào một bình luận, nhưng hóa ra nó quá dài cho việc đó ...

TL; DR
Liên kết dữ liệu có thể gây ra sự cố hiệu suất trên các trang phức tạp.


26
Vâng tôi thứ hai này. Trách nhiệm chính của ứng dụng của chúng tôi là hiển thị các kết nối giữa các thực thể khác nhau. Một trang nhất định có thể có 10 phần. Mỗi phần có một bảng. Mỗi bảng có 2-5 bộ lọc typeahead. Mỗi bảng có 2-5 cột, mỗi cột có 10 hàng. Rất nhanh chóng, chúng tôi gặp phải các vấn đề hoàn hảo và đi với các tùy chọn "thủ thuật tương tự".
Scott Silvi

10
Có công bằng không khi nói rằng Angular không chỉ liên quan đến dữ liệu và một số ứng dụng có thể không muốn sử dụng tính năng này vì chính xác những lý do mà người khác đã trích dẫn? Tôi nghĩ rằng cách tiếp cận của DI và mô-đun tự nó có giá trị rất nhiều; có phép thuật tự động ràng buộc là tốt nhưng trong mọi triển khai hiện có đều có sự đánh đổi hiệu suất. Cách thức của Angular được cho là vượt trội so với phần lớn các ứng dụng web CRUD và mọi người chỉ đang đập vào tường bằng cách cố gắng đưa nó đến mức cực đoan. Sẽ rất tốt nếu có một phương pháp lắng nghe sự kiện thay thế được hỗ trợ, nhưng có lẽ về cơ bản là quá phức tạp đối với một khung duy nhất?
Jason Boyd

8
Angular hiện có một cách và liên kết dữ liệu một lần để giúp giải quyết vấn đề này. Hơn nữa, giờ đây nó có các chỉ mục cho nguồn lặp lại của bạn, cho phép bạn sửa đổi danh sách mà không cần xây dựng lại dom cho toàn bộ nội dung.
Gaute Løken

6
@MW. Thành thật mà nói tôi nghĩ ràng buộc - một lần là trong cốt lõi. Nhưng có vẻ như không phải vậy. Đó chỉ là điều bạn có thể làm khi viết chỉ thị của riêng mình, về cơ bản là liên kết nội dung mà không cần xem chúng. Tuy nhiên, có một mod ux cho nó: github.com/pasvaz/bindonce
Gaute Løken

9
Một tiếng hét từ tương lai cho bất cứ ai đọc điều này: ràng buộc một lần bây giờ là một tính năng cốt lõi trong Angular v1.3, đọc thêm tại đây: docs.angularjs.org/guide/expression
Nobita

158

Bằng cách kiểm tra bẩn $scopeđối tượng

Angular duy trì một đơn giản arraycủa người theo dõi trong các $scopeđối tượng. Nếu bạn kiểm tra bất kỳ $scopebạn sẽ thấy rằng nó có chứa một cuộc arraygọi $$watchers.

Mỗi người theo dõi là một trong objectđó có những thứ khác

  1. Một biểu hiện mà người theo dõi đang theo dõi. Đây có thể chỉ là một attributecái tên, hoặc một cái gì đó phức tạp hơn.
  2. Một giá trị được biết đến cuối cùng của biểu thức. Điều này có thể được kiểm tra đối với giá trị tính toán hiện tại của biểu thức. Nếu các giá trị khác nhau, trình theo dõi sẽ kích hoạt chức năng và đánh dấu $scopelà bẩn.
  3. Một chức năng sẽ được thực hiện nếu người theo dõi bị bẩn.

Người theo dõi được định nghĩa như thế nào

Có nhiều cách khác nhau để định nghĩa một người theo dõi trong AngularJS.

  • Bạn có thể rõ ràng $watchmột attributetrên $scope.

    $scope.$watch('person.username', validateUnique);
  • Bạn có thể đặt một {{}}phép nội suy trong mẫu của bạn (một trình theo dõi sẽ được tạo cho bạn trên hiện tại $scope).

    <p>username: {{person.username}}</p>
  • Bạn có thể yêu cầu một chỉ thị như ng-modelxác định người theo dõi cho bạn.

    <input ng-model="person.username" />

Các $digestchu kỳ kiểm tra tất cả sát chống lại giá trị cuối cùng của họ

Khi chúng ta tương tác với AngularJS thông qua các kênh thông thường (ng-model, ng-repeat, v.v.), một chu trình tiêu hóa sẽ được kích hoạt bởi lệnh.

Một chu trình tiêu hóa là một giao dịch sâu đầu tiên $scopevà tất cả các con của nó . Đối với mỗi $scope object, chúng tôi lặp lại qua nó $$watchers arrayvà đánh giá tất cả các biểu thức. Nếu giá trị biểu thức mới khác với giá trị đã biết cuối cùng, chức năng của trình xem sẽ được gọi. Hàm này có thể biên dịch lại một phần của DOM, tính toán lại một giá trị trên $scope, kích hoạt một AJAX request, bất cứ điều gì bạn cần nó để làm.

Mọi phạm vi đều đi qua và mọi biểu thức đồng hồ được đánh giá và kiểm tra so với giá trị cuối cùng.

Nếu một người theo dõi được kích hoạt, $scopelà bẩn

Nếu một người theo dõi được kích hoạt, ứng dụng sẽ biết có gì đó đã thay đổi và $scopeđược đánh dấu là bẩn.

Các chức năng của người theo dõi có thể thay đổi các thuộc tính khác trên $scopehoặc trên cha mẹ $scope. Nếu một $watcherchức năng đã được kích hoạt, chúng tôi không thể đảm bảo rằng các chức năng khác của chúng tôi $scopevẫn sạch và vì vậy chúng tôi thực hiện lại toàn bộ chu trình phân loại.

Điều này là do AngularJS có ràng buộc hai chiều, do đó dữ liệu có thể được truyền ngược lên $scopecây. Chúng tôi có thể thay đổi một giá trị ở mức cao hơn $scopeđã được tiêu hóa. Có lẽ chúng ta thay đổi một giá trị trên $rootScope.

Nếu $digestbẩn, chúng tôi thực hiện lại toàn bộ $digestchu trình

Chúng tôi liên tục lặp qua $digestchu trình cho đến khi chu trình tiêu hóa hoàn toàn sạch (tất cả các $watchbiểu thức có cùng giá trị như trong chu kỳ trước) hoặc chúng tôi đạt đến giới hạn tiêu hóa. Theo mặc định, giới hạn này được đặt ở mức 10.

Nếu chúng tôi đạt đến giới hạn tiêu hóa, AngularJS sẽ phát sinh lỗi trong bảng điều khiển:

10 $digest() iterations reached. Aborting!

Thông báo khó về máy nhưng dễ với nhà phát triển

Như bạn có thể thấy, mỗi khi có gì đó thay đổi trong ứng dụng AngularJS, AngularJS sẽ kiểm tra từng người theo dõi trong $scopehệ thống phân cấp để xem cách phản hồi. Đối với một nhà phát triển, đây là một lợi ích lớn về năng suất, vì bây giờ bạn cần phải viết gần như không có mã nối dây, AngularJS sẽ chỉ thông báo nếu một giá trị đã thay đổi và làm cho phần còn lại của ứng dụng phù hợp với thay đổi.

Từ quan điểm của máy mặc dù điều này rất kém hiệu quả và sẽ làm chậm ứng dụng của chúng tôi nếu chúng tôi tạo quá nhiều người theo dõi. Misko đã trích dẫn một con số khoảng 4000 người theo dõi trước khi ứng dụng của bạn cảm thấy chậm trên các trình duyệt cũ hơn.

Giới hạn này là dễ dàng để đạt được nếu bạn ng-repeatvượt quá lớn JSON arraychẳng hạn. Bạn có thể giảm thiểu việc này bằng các tính năng như ràng buộc một lần để biên dịch mẫu mà không cần tạo trình theo dõi.

Làm thế nào để tránh tạo quá nhiều người theo dõi

Mỗi lần người dùng của bạn tương tác với ứng dụng của bạn, mỗi người theo dõi trong ứng dụng của bạn sẽ được đánh giá ít nhất một lần. Một phần lớn của việc tối ưu hóa ứng dụng AngularJS là giảm số lượng người theo dõi trong $scopecây của bạn . Một cách dễ dàng để làm điều này là với một lần ràng buộc .

Nếu bạn có dữ liệu hiếm khi thay đổi, bạn chỉ có thể liên kết dữ liệu đó một lần bằng cách sử dụng cú pháp :: như vậy:

<p>{{::person.username}}</p>

hoặc là

<p ng-bind="::person.username"></p>

Liên kết sẽ chỉ được kích hoạt khi mẫu chứa được hiển thị và dữ liệu được tải vào $scope.

Điều này đặc biệt quan trọng khi bạn có một ng-repeatvới nhiều mặt hàng.

<div ng-repeat="person in people track by username">
  {{::person.username}}
</div>

Cảm ơn @ user2864740 - mặc dù câu trả lời của Misko là đúng. Anh ta hiểu rõ khuôn khổ hơn bất kỳ ai, và thật tuyệt khi anh ta tham gia với Stack Overflow ..
siêu sáng

4
Tôi không đồng ý rằng câu trả lời nên được đặt lên hàng đầu; có một sự khác biệt giữa việc biết một cái gì đó và viết một câu trả lời có liên quan / chi tiết cho một câu hỏi cụ thể. Có nhiều cách tốt hơn để nhận được giải thưởng. Dù sao ..
dùng2864740

1
Tôi không nghi ngờ điều đó là đúng, nhưng câu hỏi và câu trả lời câu trả lời :)
user2864740

3
Câu trả lời hay bao gồm cách kiểm tra bẩn hành xử và những gì nó thực sự đánh giá, một điều không quá rõ ràng trong câu trả lời của Misko.
strider

3
Câu trả lời tuyệt vời và chi tiết. @superluminary, cảm ơn câu trả lời như vậy. Hơn nữa, sau khi đọc câu trả lời này, tôi đi đến điểm rằng chúng ta không được thêm biểu thức không bình thường như một biểu thức đang được xem.
Mangu Singh Rajpurohit

81

Đây là sự hiểu biết cơ bản của tôi. Nó cũng có thể sai!

  1. Các mục được theo dõi bằng cách chuyển một hàm (trả lại thứ cần xem) cho $watchphương thức.
  2. Thay đổi đối với các mục đã xem phải được thực hiện trong một khối mã được gói theo $applyphương thức.
  3. Vào cuối của $applycác $digestphương pháp được gọi mà đi qua mỗi đồng hồ và kiểm tra xem nếu họ thay đổi kể từ lần cuối cùng $digestran.
  4. Nếu bất kỳ thay đổi nào được tìm thấy thì thông báo sẽ được gọi lại cho đến khi tất cả các thay đổi ổn định.

Trong phát triển bình thường, cú pháp liên kết dữ liệu trong HTML sẽ cho trình biên dịch AngularJS tạo đồng hồ cho bạn và các phương thức điều khiển đã được chạy bên trong $apply. Vì vậy, với các nhà phát triển ứng dụng, tất cả đều minh bạch.


4
Khi nào phương thức áp dụng được kích hoạt?
numan salati

3
@EliseuMonar Vòng lặp digest chạy do kết quả của một số sự kiện hoặc gọi $ áp dụng (), nó không được gọi định kỳ dựa trên bộ đếm thời gian. thấy như thế nào $ đồng hồ chức năng làm việc AngularJS không? làm thế nào để liên kết và tiêu hóa hoạt động trong AngularJS?
adl

1
@remi, tôi không quan tâm đến phiên bản cuối cùng của AngularJS. Có phải họ đã sử dụng proxy hoặc Object.observe? Nếu không, chúng vẫn đang trong kỷ nguyên kiểm tra bẩn, xây dựng một vòng lặp thời gian để xem các thuộc tính mô hình đã thay đổi.
Eliseu Monar dos Santos

1
Tôi đã đọc rằng tiêu hóa sẽ chạy tối đa là mười lần sitepoint.com/understanding-angulars-apply-digest
user137717

62

Tôi tự hỏi điều này một thời gian. Không có setters làm thế nào để AngularJSthông báo thay đổi $scopeđối tượng? Nó có thăm dò ý kiến ​​của họ không?

Những gì nó thực sự là đây: Bất kỳ vị trí "bình thường" nào bạn sửa đổi mô hình đã được gọi từ ruột của AngularJSnó, vì vậy nó sẽ tự động gọi $applycho bạn sau khi mã của bạn chạy. Giả sử bộ điều khiển của bạn có một phương thức được nối với ng-clickmột số phần tử. Bởi vì kết AngularJSnối việc gọi phương thức đó với bạn, nó có cơ hội thực hiện $applyở một nơi thích hợp. Tương tự như vậy, đối với các biểu thức xuất hiện ngay trong các khung nhìn, chúng được thực thi bởi AngularJSvì vậy nó thực hiện $apply.

Khi tài liệu nói về việc phải gọi $applythủ công cho mã bên ngoàiAngularJS , nó nói về mã mà khi chạy, nó không xuất phát từ AngularJSchính nó trong ngăn xếp cuộc gọi.


32

Giải thích với hình ảnh:

Liên kết dữ liệu cần ánh xạ

Tham chiếu trong phạm vi không chính xác là tham chiếu trong mẫu. Khi bạn liên kết dữ liệu với hai đối tượng, bạn cần một đối tượng thứ ba lắng nghe đối tượng đầu tiên và sửa đổi đối tượng kia.

nhập mô tả hình ảnh ở đây

Tại đây, khi bạn sửa đổi <input>, bạn chạm vào data-ref3 . Và mecanism liên kết dữ liệu cổ điển sẽ thay đổi data-ref4 . Vậy làm thế nào các {{data}}biểu thức khác sẽ di chuyển?

Sự kiện dẫn đến $ digest ()

nhập mô tả hình ảnh ở đây

Angular duy trì một oldValuenewValuecủa mọi ràng buộc. Và sau mỗi sự kiện Angular , $digest()vòng lặp nổi tiếng sẽ kiểm tra WatchList để xem có gì thay đổi không. Những sự kiện gócng-click, ng-change, $httphoàn thành ... Các $digest()ý loop chừng như bất kỳ oldValuekhác với các newValue.

Trong hình trước, nó sẽ nhận thấy rằng data-ref1 và data-ref2 đã thay đổi.

Kết luận

Nó hơi giống trứng và gà. Bạn không bao giờ biết ai bắt đầu, nhưng hy vọng nó hoạt động hầu hết thời gian như mong đợi.

Điểm khác là bạn có thể dễ dàng hiểu được tác động sâu sắc của một ràng buộc đơn giản trên bộ nhớ và CPU. Hy vọng Máy tính để bàn đủ béo để xử lý việc này. Điện thoại di động không mạnh đến thế.


22

Rõ ràng là không có kiểm tra định kỳ về Scopeviệc có bất kỳ thay đổi nào trong các Đối tượng được đính kèm hay không. Không phải tất cả các đối tượng gắn liền với phạm vi được xem. Phạm vi nguyên mẫu duy trì một người theo dõi $$ . Scopechỉ lặp đi lặp lại thông qua điều này $$watcherskhi $digestđược gọi.

Angular thêm một người theo dõi cho người theo dõi $$ cho mỗi người trong số này

  1. {{biểu thức}} - Trong các mẫu của bạn (và bất kỳ nơi nào khác có biểu thức) hoặc khi chúng tôi xác định mô hình ng.
  2. $ scope. $ watch ('biểu thức / hàm') - Trong JavaScript của bạn, chúng ta chỉ cần đính kèm một đối tượng phạm vi cho góc để xem.

Hàm đồng hồ $ có ba tham số:

  1. Đầu tiên là một hàm watcher chỉ trả về đối tượng hoặc chúng ta chỉ có thể thêm một biểu thức.

  2. Thứ hai là một hàm nghe sẽ được gọi khi có sự thay đổi trong đối tượng. Tất cả những điều như thay đổi DOM sẽ được thực hiện trong chức năng này.

  3. Thứ ba là một tham số tùy chọn trong một boolean. Nếu đúng, góc sâu của nó quan sát đối tượng & nếu Angular giả của nó chỉ thực hiện tham chiếu xem đối tượng. Thực hiện thô sơ của đồng hồ $ trông như thế này

Scope.prototype.$watch = function(watchFn, listenerFn) {
   var watcher = {
       watchFn: watchFn,
       listenerFn: listenerFn || function() { },
       last: initWatchVal  // initWatchVal is typically undefined
   };
   this.$$watchers.push(watcher); // pushing the Watcher Object to Watchers  
};

Có một điều thú vị trong Angular gọi là Digest Chu kỳ. Chu trình $ digest bắt đầu là kết quả của một cuộc gọi đến $ scope. $ Digest (). Giả sử rằng bạn thay đổi mô hình $ scope trong hàm xử lý thông qua lệnh ng-click. Trong trường hợp đó, AngularJS tự động kích hoạt chu trình $ digest bằng cách gọi $ digest (). Ngoài ng-click, có một số chỉ thị / dịch vụ tích hợp khác cho phép bạn thay đổi mô hình (ví dụ: ng-model, $ timeout, v.v.) và tự động kích hoạt chu trình $ digest. Việc triển khai thô của $ digest trông như thế này.

Scope.prototype.$digest = function() {
      var dirty;
      do {
          dirty = this.$$digestOnce();
      } while (dirty);
}
Scope.prototype.$$digestOnce = function() {
   var self = this;
   var newValue, oldValue, dirty;
   _.forEach(this.$$watchers, function(watcher) {
          newValue = watcher.watchFn(self);
          oldValue = watcher.last;   // It just remembers the last value for dirty checking
          if (newValue !== oldValue) { //Dirty checking of References 
   // For Deep checking the object , code of Value     
   // based checking of Object should be implemented here
             watcher.last = newValue;
             watcher.listenerFn(newValue,
                  (oldValue === initWatchVal ? newValue : oldValue),
                   self);
          dirty = true;
          }
     });
   return dirty;
 };

Nếu chúng tôi sử dụng hàm setTimeout () của JavaScript để cập nhật mô hình phạm vi, Angular không có cách nào để biết bạn có thể thay đổi điều gì. Trong trường hợp này, trách nhiệm của chúng tôi là gọi $ áp dụng () theo cách thủ công, điều này kích hoạt chu trình $ digest. Tương tự, nếu bạn có một lệnh thiết lập trình nghe sự kiện DOM và thay đổi một số mô hình bên trong hàm xử lý, bạn cần gọi $ áp dụng () để đảm bảo các thay đổi có hiệu lực. Ý tưởng lớn của $ áp dụng là chúng ta có thể thực thi một số mã không biết về Angular, mã đó vẫn có thể thay đổi mọi thứ trên phạm vi. Nếu chúng tôi bọc mã đó bằng $ áp dụng, sẽ lo việc gọi $ digest (). Thực hiện thô sơ của $ áp dụng ().

Scope.prototype.$apply = function(expr) {
       try {
         return this.$eval(expr); //Evaluating code in the context of Scope
       } finally {
         this.$digest();
       }
};

15

AngularJS xử lý cơ chế liên kết dữ liệu với sự trợ giúp của ba chức năng mạnh mẽ: $ watch () , $ digest ()$ áp dụng () . Hầu hết thời gian AngularJS sẽ gọi $ scope. $ Watch () và $ scope. $ Digest (), nhưng trong một số trường hợp, bạn có thể phải gọi các hàm này theo cách thủ công để cập nhật các giá trị mới.

$ xem () : -

Hàm này được sử dụng để quan sát các thay đổi trong một biến trên phạm vi $. Nó chấp nhận ba tham số: biểu thức, đối tượng nghe và đối tượng đẳng thức, trong đó đối tượng lắng nghe và đẳng thức là các tham số tùy chọn.

$ digest () -

Hàm này lặp qua tất cả các đồng hồ trong đối tượng $ scope và các đối tượng $ scope con của nó
(nếu nó có bất kỳ). Khi $ digest () lặp lại trên đồng hồ, nó sẽ kiểm tra xem giá trị của biểu thức có thay đổi hay không. Nếu giá trị đã thay đổi, AngularJS gọi người nghe bằng giá trị mới và giá trị cũ. Hàm $ digest () được gọi bất cứ khi nào AngularJS nghĩ rằng nó là cần thiết. Ví dụ: sau khi nhấp vào nút hoặc sau cuộc gọi AJAX. Bạn có thể có một số trường hợp AngularJS không gọi hàm $ digest () cho bạn. Trong trường hợp đó bạn phải tự gọi nó.

$ áp dụng () -

Angular chỉ tự động cập nhật một cách kỳ diệu những thay đổi mô hình bên trong bối cảnh AngularJS. Khi bạn thay đổi bất kỳ mô hình nào bên ngoài bối cảnh Angular (như các sự kiện DOM của trình duyệt, setTimeout, XHR hoặc thư viện của bên thứ ba), thì bạn cần thông báo cho Angular về các thay đổi bằng cách gọi thủ công $ áp dụng (). Khi cuộc gọi hàm $ application () kết thúc, AngularJS gọi $ digest () bên trong, vì vậy tất cả các ràng buộc dữ liệu được cập nhật.


7

Nó xảy ra rằng tôi cần liên kết một mô hình dữ liệu của một người với một biểu mẫu, những gì tôi đã làm là một ánh xạ trực tiếp của dữ liệu với biểu mẫu.

Ví dụ: nếu mô hình có cái gì đó như:

$scope.model.people.name

Đầu vào điều khiển của biểu mẫu:

<input type="text" name="namePeople" model="model.people.name">

Theo cách đó, nếu bạn sửa đổi giá trị của bộ điều khiển đối tượng, điều này sẽ được phản ánh tự động trong chế độ xem.

Một ví dụ mà tôi đã chuyển mô hình được cập nhật từ dữ liệu máy chủ là khi bạn yêu cầu mã zip và mã zip dựa trên tải bằng văn bản một danh sách các thuộc địa và thành phố được liên kết với chế độ xem đó và theo mặc định, đặt giá trị đầu tiên với người dùng. Và điều này tôi đã làm việc rất tốt, những gì xảy ra, là angularJSđôi khi mất vài giây để làm mới mô hình, để làm điều này, bạn có thể đặt một vòng quay trong khi hiển thị dữ liệu.


14
Tôi đã đọc câu trả lời này 5 lần và tôi vẫn không hiểu ý nghĩa của nó ở đây.
sbedulin

1
Câu trả lời có vẻ như câu đố đối với tôi
Aman

6
  1. Liên kết dữ liệu một chiều là một cách tiếp cận trong đó một giá trị được lấy từ mô hình dữ liệu và được chèn vào một phần tử HTML. Không có cách nào để cập nhật mô hình từ xem. Nó được sử dụng trong các hệ thống mẫu cổ điển. Các hệ thống này liên kết dữ liệu theo một hướng.

  2. Liên kết dữ liệu trong các ứng dụng Angular là tự động đồng bộ hóa dữ liệu giữa mô hình và các thành phần xem.

Ràng buộc dữ liệu cho phép bạn coi mô hình là nguồn đơn chân lý trong ứng dụng của mình. Khung nhìn là hình chiếu của mô hình mọi lúc. Nếu mô hình được thay đổi, khung nhìn phản ánh sự thay đổi và ngược lại.


5

Dưới đây là một ví dụ về liên kết dữ liệu với AngularJS, sử dụng trường đầu vào. Tôi sẽ giải thích sau

Mã HTML

<div ng-app="myApp" ng-controller="myCtrl" class="formInput">
     <input type="text" ng-model="watchInput" Placeholder="type something"/>
     <p>{{watchInput}}</p> 
</div>

Mã AngularJS

myApp = angular.module ("myApp", []);
myApp.controller("myCtrl", ["$scope", function($scope){
  //Your Controller code goes here
}]);

Như bạn có thể thấy trong ví dụ trên, AngularJS sử dụng ng-modelđể lắng nghe và xem những gì xảy ra trên các phần tử HTML, đặc biệt là trên inputcác trường. Khi một cái gì đó xảy ra, làm một cái gì đó. Trong trường hợp của chúng tôi, ng-modelđược liên kết với quan điểm của chúng tôi, bằng cách sử dụng ký hiệu ria mép {{}}. Bất cứ thứ gì được gõ bên trong trường đầu vào đều được hiển thị trên màn hình ngay lập tức. Và đó là vẻ đẹp của liên kết dữ liệu, sử dụng AngularJS ở dạng đơn giản nhất.

Hi vọng điêu nay co ich.

Xem một ví dụ hoạt động ở đây trên Codepen


5

AngularJs hỗ trợ ràng buộc dữ liệu hai chiều .
Có nghĩa là bạn có thể truy cập dữ liệu Xem -> Bộ điều khiển & Bộ điều khiển -> Xem

Ví dụ

1)

// If $scope have some value in Controller. 
$scope.name = "Peter";

// HTML
<div> {{ name }} </div>

O / P

Peter

Bạn có thể liên kết dữ liệu trong ng-modelLike: -
2)

<input ng-model="name" />

<div> {{ name }} </div>

Ở đây trong ví dụ trên bất cứ điều gì người dùng đầu vào sẽ cung cấp, Nó sẽ được hiển thị trong <div>thẻ.

Nếu muốn liên kết đầu vào từ html đến bộ điều khiển: -
3)

<form name="myForm" ng-submit="registration()">
   <label> Name </lbel>
   <input ng-model="name" />
</form>

Ở đây nếu bạn muốn sử dụng đầu vào nametrong bộ điều khiển thì,

$scope.name = {};

$scope.registration = function() {
   console.log("You will get the name here ", $scope.name);
};

ng-modelliên kết quan điểm của chúng tôi và đưa ra biểu hiện {{ }}.
ng-modellà dữ liệu được hiển thị cho người dùng trong chế độ xem và người dùng tương tác với nhau.
Vì vậy, rất dễ dàng để liên kết dữ liệu trong AngularJs.


4

Angular.js tạo trình theo dõi cho mọi mô hình chúng tôi tạo trong chế độ xem. Bất cứ khi nào một mô hình được thay đổi, một lớp "ng-bẩn" sẽ được gắn vào mô hình đó, vì vậy người theo dõi sẽ quan sát tất cả các mô hình có lớp "ng-bẩn" và cập nhật các giá trị của chúng trong bộ điều khiển & ngược lại.


3

ràng buộc dữ liệu:

Ràng buộc dữ liệu là gì?

Bất cứ khi nào người dùng thay đổi dữ liệu trong chế độ xem, sẽ xảy ra cập nhật về thay đổi đó trong mô hình phạm vi và ngược lại.

Làm thế nào là nó có thể?

Câu trả lời ngắn: Với sự giúp đỡ của chu trình tiêu hóa.

Mô tả: Angs js đặt trình theo dõi trên mô hình phạm vi, kích hoạt chức năng nghe nếu có thay đổi trong mô hình.

$scope.$watch('modelVar' , function(newValue,oldValue){

// Mã cập nhật Dom với giá trị mới

});

Vậy chức năng watcher được gọi là khi nào và như thế nào?

Chức năng Watcher được gọi là một phần của chu trình tiêu hóa.

Chu trình tiêu hóa được gọi tự động được kích hoạt như một phần của js góc được xây dựng trong các chỉ thị / dịch vụ như ng-model, ng-bind, $ timeout, ng-click và các thứ khác .. cho phép bạn kích hoạt chu trình tiêu hóa.

Chức năng chu trình tiêu hóa:

$scope.$digest() -> digest cycle against the current scope.
$scope.$apply() -> digest cycle against the parent scope 

I E$rootScope.$apply()

Lưu ý: $ application () bằng với $ rootScope. $ Digest () điều này có nghĩa là việc kiểm tra bẩn bắt đầu ngay từ gốc hoặc trên cùng hoặc phạm vi cha mẹ xuống tất cả các phạm vi $ con trong ứng dụng js góc.

Các tính năng trên hoạt động trong trình duyệt IE cho các phiên bản được đề cập cũng chỉ bằng cách đảm bảo ứng dụng của bạn là ứng dụng js góc, có nghĩa là bạn đang sử dụng tệp tập lệnh khung angularjs được tham chiếu trong thẻ script.

Cảm ơn bạn.

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.