Làm cách nào để bỏ qua tải ban đầu khi xem các thay đổi mô hình trong AngularJS?


183

Tôi có một trang web đóng vai trò là trình soạn thảo cho một thực thể duy nhất, nằm dưới dạng biểu đồ sâu trong thuộc tính $ scope.fieldcontainer. Sau khi tôi nhận được phản hồi từ API REST (thông qua tài nguyên $), tôi thêm đồng hồ vào 'người điều khiển trường'. Tôi đang sử dụng đồng hồ này để phát hiện xem trang / thực thể có "bẩn" không. Ngay bây giờ tôi đang làm cho nút lưu bị trả lại nhưng thực sự tôi muốn làm cho nút lưu trở nên vô hình cho đến khi người dùng điều chỉnh mô hình.

Những gì tôi nhận được là một kích hoạt duy nhất của đồng hồ, điều mà tôi nghĩ đang xảy ra bởi vì .fieldcontainer = ... việc chuyển nhượng diễn ra ngay sau khi tôi tạo đồng hồ. Tôi đã nghĩ đến việc chỉ sử dụng một tài sản "DirtCount" để hấp thụ báo động sai ban đầu nhưng cảm giác rất khó chịu ... và tôi nghĩ rằng phải có một cách "thành ngữ góc cạnh" để đối phó với điều này - tôi không phải là người duy nhất sử dụng đồng hồ để phát hiện một mô hình bẩn.

Đây là mã nơi tôi đặt đồng hồ:

 $scope.fieldcontainer = Message.get({id: $scope.entityId },
            function(message,headers) {
                $scope.$watch('fieldcontainer',
                    function() {
                        console.log("model is dirty.");
                        if ($scope.visibility.saveButton) {
                            $('#saveMessageButtonRow').effect("bounce", { times:5, direction: 'right' }, 300);
                        }
                    }, true);
            });

Tôi chỉ nghĩ rằng phải có một cách sạch hơn để làm điều này hơn là bảo vệ mã "UI bẩn" của tôi bằng một "if (DirtCount> 0)" ...


Tôi cũng cần có khả năng tải lại $ scope.fieldcontainer dựa trên các nút nhất định (ví dụ: tải phiên bản trước của thực thể). Đối với điều này, tôi sẽ cần phải treo đồng hồ, tải lại, sau đó tiếp tục đồng hồ.
Kevin Hoffman

Bạn có xem xét việc thay đổi câu trả lời của tôi là câu trả lời được chấp nhận không? Những người dành thời gian để đọc tất cả các cách có xu hướng đồng ý rằng đó là giải pháp chính xác.
trixtur

@trixtur Tôi có cùng một vấn đề OP, nhưng trong trường hợp của tôi, câu trả lời của bạn không hoạt động, vì giá trị cũ của tôi không có undefined. Nó có một giá trị mặc định là cần thiết trong trường hợp cập nhật mô hình của tôi không đưa ra tất cả thông tin. Vì vậy, một số giá trị không thay đổi mà phải kích hoạt.
oliholz

@oliholz ​​Vì vậy, thay vì kiểm tra chống lại không xác định, hãy lấy "typeof" và kiểm tra old_fieldcontainer so với giá trị mặc định của bạn.
trixtur

@KevinHoffman bạn nên đánh dấu câu trả lời của MW vào câu trả lời đúng cho những người khác tìm thấy câu hỏi này. Đó là một giải pháp tốt hơn nhiều. Cảm ơn!
Dev01

Câu trả lời:


117

đặt cờ ngay trước tải ban đầu,

var initializing = true

và sau đó khi chiếc đồng hồ $ đầu tiên cháy, hãy làm

$scope.$watch('fieldcontainer', function() {
  if (initializing) {
    $timeout(function() { initializing = false; });
  } else {
    // do whatever you were going to do
  }
});

Cờ sẽ bị xé xuống vào cuối chu kỳ tiêu hóa hiện tại, vì vậy thay đổi tiếp theo sẽ không bị chặn.


17
Vâng, điều đó sẽ hiệu quả nhưng việc so sánh cách tiếp cận giá trị cũ và mới được đề xuất bởi @MW. là cả hai thành ngữ Angular đơn giản và nhiều hơn. Bạn có thể cập nhật câu trả lời được chấp nhận?
gerryster

2
Luôn đặt giá trị cũ bằng với giá trị mới trong lần bắn đầu tiên được thêm vào một cách cụ thể để tránh phải thực hiện hack cờ này
Charlie Martin

2
Câu trả lời của MW thực sự không chính xác vì giá trị mới cho giá trị cũ sẽ không xử lý trong trường hợp giá trị cũ không được xác định.
trixtur

1
Đối với người bình luận: đây không phải là mô hình thần, không có gì ngăn cản bạn đặt biến 'khởi tạo' trong phạm vi của trang trí, sau đó trang trí đồng hồ "bình thường" của bạn với nó. Trong mọi trường hợp đó hoàn toàn nằm ngoài phạm vi của câu hỏi này.
viết lại

1
Xem plnkr.co/edit/VrWrHHWwukqnxaEM3J5i để so sánh các phương thức được đề xuất, cả hai đều thiết lập trình theo dõi trong cuộc gọi lại .get () và khởi tạo phạm vi.
viết lại

482

Lần đầu tiên người nghe được gọi, giá trị cũ và giá trị mới sẽ giống hệt nhau. Vì vậy, chỉ cần làm điều này:

$scope.$watch('fieldcontainer', function(newValue, oldValue) {
  if (newValue !== oldValue) {
    // do whatever you were going to do
  }
});

Đây thực sự là cách các tài liệu Angular khuyên bạn nên xử lý nó :

Sau khi người theo dõi được đăng ký với phạm vi, fn của người nghe được gọi là không đồng bộ (thông qua $ evalAsync) để khởi tạo trình theo dõi. Trong những trường hợp hiếm hoi, điều này là không mong muốn vì người nghe được gọi khi kết quả của watchExpression không thay đổi. Để phát hiện kịch bản này trong fn của người nghe, bạn có thể so sánh newVal và oldVal. Nếu hai giá trị này giống hệt nhau (===) thì trình nghe được gọi do khởi tạo


3
cách này là thành ngữ hơn câu trả lời của @ viết lại
Jiří Vypědřík

25
Điều này LAF không đúng. Điểm quan trọng là bỏ qua thay đổi từ nullgiá trị được tải ban đầu, không bỏ qua thay đổi khi không có thay đổi.
viết lại

1
Chà, chức năng nghe sẽ luôn kích hoạt khi đồng hồ được tạo. Điều này bỏ qua cuộc gọi đầu tiên, đó là những gì tôi tin rằng người hỏi muốn. Nếu anh ấy đặc biệt muốn bỏ qua sự thay đổi khi giá trị cũ là null, tất nhiên anh ấy có thể so sánh oldValue với null ... nhưng tôi không nghĩ đó là điều anh ấy muốn làm.
MW.

OP đặc biệt hỏi về người theo dõi về tài nguyên (từ các dịch vụ REST). Các tài nguyên được tạo trước tiên, sau đó các trình theo dõi được áp dụng, và sau đó các thuộc tính từ phản hồi được viết và chỉ sau đó Angular tạo ra một chu kỳ. Bỏ qua chu kỳ đầu tiên với cờ "khởi tạo" cung cấp cách chỉ áp dụng trình theo dõi trên các tài nguyên được khởi tạo, nhưng vẫn theo dõi các thay đổi từ / đến null.
viết lại

7
Điều này là sai, oldValuesẽ là null trong lần đi đầu tiên.
Kevin C.

42

Tôi nhận ra câu hỏi này đã được trả lời, tuy nhiên tôi có một gợi ý:

$scope.$watch('fieldcontainer', function (new_fieldcontainer, old_fieldcontainer) {
    if (typeof old_fieldcontainer === 'undefined') return;

    // Other code for handling changed object here.
});

Sử dụng cờ hoạt động nhưng có một chút mùi mã cho nó bạn không nghĩ?


1
Đây là con đường để đi. Trong Coffeescript, nó đơn giản như return unless oldValue?(? Là toán tử hiện sinh trong CS).
Kevin C.

1
Đạo cụ về mùi mã từ! cũng kiểm tra đối tượng thần lol. quá tốt.
Matthew Harwood

1
Mặc dù đây không phải là câu trả lời được chấp nhận, nhưng điều này làm cho mã của tôi hoạt động khi điền một đối tượng từ API và tôi muốn xem các trạng thái lưu khác nhau. Rất đẹp.
Lucas Reppe Welander

1
Cảm ơn - điều này đã giúp tôi
Wand Maker

1
Điều này thật tuyệt, tuy nhiên trong trường hợp của tôi, tôi đã khởi tạo dữ liệu dưới dạng một mảng trống trước khi thực hiện lệnh gọi AJAX tới API của mình, vì vậy hãy kiểm tra độ dài của dữ liệu đó thay vì gõ. Nhưng câu trả lời này chắc chắn cho thấy phương pháp hiệu quả nhất ít nhất.
Saborknight

4

Trong quá trình tải ban đầu của các giá trị hiện tại, trường giá trị cũ không được xác định. Vì vậy, ví dụ dưới đây giúp bạn loại trừ tải ban đầu.

$scope.$watch('fieldcontainer', 
  function(newValue, oldValue) {
    if (newValue && oldValue && newValue != oldValue) {
      // here what to do
    }
  }), true;

Bạn có thể muốn sử dụng! == kể từ khi nullundefinedsẽ khớp trong tình huống này hoặc ( '1' == 1v.v.)
Jamie Pate

nói chung bạn đúng. Nhưng trong trường hợp đó, newValue và oldValue không thể là các giá trị góc như vậy vì các kiểm tra trước đó. Cả hai giá trị có cùng loại cũng vì thuộc cùng một trường. Cảm ơn bạn.
Tahsin Turkoz

3

Chỉ cần xác nhận trạng thái của val mới:

$scope.$watch('fieldcontainer',function(newVal) {
      if(angular.isDefined(newVal)){
          //Do something
      }
});
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.