Jak działa wiązanie danych w ramach AngularJS?

Nie znalazłem szczegółów technicznych na ich witryny. Jest mniej lub bardziej jasny, w jaki sposób działa, gdy dane są propagowane od widoku do modelu. Ale w jaki sposób AngularJs śledzi zmiany właściwości modelu bez osadników i getters?

Odkryłem, że są Obserwatory JavaScript, które mogą wykonać tę pracę. Ale nie są obsługiwane w Internet Explorer 6 i Internet Explorer 7. Jak więc AngularJs wiedzą, że zmieniłem na przykład następujące i odzwierciedlało tę zmianę w widoku?

myobject.myproperty="new value";
2012
Pashec 13 marzec 2012, 14:16

11 odpowiedzi

Najlepsza odpowiedź

AngularJs pamięta wartość i porównuje ją do poprzedniej wartości. Jest to podstawowe brudne sprawdzanie. Jeśli istnieje zmiana wartości, wystrzewa zdarzenie zmiany.

Metoda {X0}}, która jest tym, co nazywasz, gdy przejdziesz z świata non-angularjs do świata Angularjs, dzwoni $digest(). Digest jest po prostu zwykłym brudnym sprawdzaniem. Działa na wszystkich przeglądarkach i jest całkowicie przewidywalny.

Aby kontrastować brudne sprawdzanie (angularjs) vs słuchacze zmiany (knocOuttjs i backbone.js ) : Podczas sprawdzania brudny może wydawać się proste, a nawet nieskuteczne ( zajmę się tym później) , okazuje się, że jest poprawny semantycznie przez cały czas, Podczas gdy myśli słuchacze mają wiele dziwnych przypadków narożnych i potrzebują takich rzeczy jak śledzenie zależności, aby uczynić go bardziej poprawnym semantycznie. Usuwanie zależności NOCKOCTJS to sprytna funkcja problemu, który nie ma Angularjs.

Problemy ze zmianą słuchaczy:

  • Składnia jest okropna, ponieważ przeglądarki nie obsługują go natywnie. Tak, są proxy, ale nie są semantycznie poprawne we wszystkich przypadkach i oczywiście nie ma pełnomocników na starych przeglądarkach. Dolna linia jest taka, że brudne sprawdzanie umożliwia wykonywanie pojo, podczas gdy knocOutJs i backbone.js zmusza cię do dziedziczenia z ich Zajęcia i uzyskaj dostęp do swoich danych za pomocą akcesoriów.
  • Zmienić koalescencję. Przypuśćmy, że masz szereg przedmiotów. Powiedz, że chcesz dodać przedmioty do tablicy, ponieważ zapętlasz się, aby dodać, za każdym razem, gdy dodasz zdarzenia podczas zmiany, co renderuje UI. Jest to bardzo złe dla wydajności. Co chcesz, aby zaktualizować UI tylko raz, na końcu. Zdarzenia zmian są zbyt drobne.
  • Zmień słuchacze ogień natychmiast na seter, który jest problemem, ponieważ słuchacz Zmień może dalsze zmienić dane, które wystrzeliwują więcej zdarzeń zmiany. To jest złe, ponieważ na stosie można mieć kilka zdarzeń zmian od razu. Przypuśćmy, że masz dwie tablice, które muszą być przechowywane w synchronizacji z jakiegokolwiek powodu. Możesz dodać tylko do jednego lub drugiego, ale za każdym razem, gdy dodasz Cię ogień zdarzenie zmiany, który ma teraz niespójny widok na świat. Jest to bardzo podobny problem do blokowania gwintów, który JavaScript pozwala uniknąć, ponieważ każde wywołanie zwrotne wykonuje wyłącznie i do zakończenia. Zmień rozbicie wydarzeń, ponieważ osadnicy mogą mieć daleko idące konsekwencje, które nie są przeznaczone i nie oczywiste, co powoduje ponownie problem wątku. Okazuje się, że to, co chcesz zrobić, jest opóźnienie wykonania wykonawcy i gwarancji, że tylko jeden słuchacz działa na raz, stąd każdy kod może swobodnie zmienić dane, a nie wie, że żaden inny kod działa, gdy to robi .

Co z wydajnością?

Może się więc wydawać, że jesteśmy powoli, ponieważ brudne sprawdzanie jest nieefektywne. Tutaj musimy spojrzeć na liczby rzeczywiste, a nie tylko mieć teoretyczne argumenty, ale najpierw definiujmy niektóre ograniczenia.

Ludzie są:

  • powolny - wszystko szybsze niż 50 ms jest niezauważalne dla ludzi, a zatem można uznać za "natychmiastowe".

  • ograniczone - Nie możesz naprawdę pokazać więcej niż około 2000 informacji na temat człowieka na jednej stronie. Coś więcej niż to jest naprawdę zły interfejs użytkownika, a ludzie nie mogą tak przetworzyć.

Więc prawdziwe pytanie brzmi: Ile porównań możesz zrobić w przeglądarce w 50 ms? Jest to trudne pytanie, aby odpowiedzieć, ponieważ wchodzi w grę, ale tutaj jest sprawa testowa: http://jsperf.com/angularjs -Digest / 6, który tworzy 10 000 obserwatorów. W nowoczesnej przeglądarce zajmuje to tylko 6 ms. Na Internet Explorer 8 zajmuje około 40 ms. Jak widać, to nie jest problem nawet w powolnych przeglądarkach w dzisiejszych czasach. Jest zastrzeżenie: porównania muszą być proste pasujące do limitu czasu ... Niestety jest to zbyt łatwe, aby dodać powolne porównania do Angularjs, więc łatwo jest zbudować powolne aplikacje, gdy nie wiesz, co ty robią. Ale mamy nadzieję, że masz odpowiedź, zapewniając moduł oprzyrządowania, który pokazałby, które są powolne porównania.

Okazuje się, że gry wideo i GPU używają podejścia brudnego sprawdzania, szczególnie dlatego, że jest spójny. Dopóki przechodzą na szybkość odświeżania monitora (zazwyczaj 50-60 Hz, lub co 16.6-20 ms), wszelkie wyniki, które są odpadami, więc lepiej wyciągając więcej rzeczy, niż wyżej FPS.

2753
Peter Mortensen 20 grudzień 2015, 21:39

Misko dał już doskonały opis sposobu działania wiązania danych, ale chciałbym dodać mój pogląd na problem z wiązaniem danych.

Jak stwierdził Misko, około 2000 wiązań należą do tego, gdzie zaczniesz widzieć problemy, ale tak nie powinieneś mieć więcej niż 2000 informacji na stronie na stronie. Może to być prawdą, ale nie każde wiązanie danych jest widoczne dla użytkownika. Po rozpoczęciu budowania dowolnego widgetu lub sieci danych z dwukierunkowym wiązaniem można łatwo łatwo Hit 2000, bez złego UX.

Rozważmy na przykład pole kombi, w którym można wpisać tekst, aby filtrować dostępne opcje. Ten rodzaj kontroli może mieć ~ 150 pozycji i nadal być wysoce użyteczne. Jeśli ma pewną dodatkową funkcję (na przykład określoną klasę w aktualnie wybranej opcji), zaczynasz uzyskać 3-5 powiązań na opcję. Umieść trzy te widżety na stronie (np. Wybór kraju, drugi, aby wybrać miasto w wspomnianym kraju, a trzeci wybrać hotel) i masz już gdzieś między 1000 a 2000 wiązaniami.

Lub rozważ kartę danych w aplikacji korporacyjnej. 50 rzędów na stronie nie jest nierozsądne, z których każdy może mieć 10-20 kolumn. Jeśli budujesz to za pomocą NG-powtań i / lub posiadać informacje w niektórych komórkach, które wykorzystują pewne wiązania, można zbliżyć się do 2000 wiązań w tej siatce sam.

Uważam, że jest to ogromny Problem podczas pracy z Angularjs, a jedynym rozwiązaniem, które udało mi się znaleźć, jest skonstruowanie widżetów bez użycia wiązania dwukierunkowego, zamiast używać nongonce, wyrejestrowanie Obserwatorzy i podobne sztuczki lub konstruują dyrektywy, które budują dom z jQuery i dom manipulacją. Czuję, że to pokonuje cel używania kątowego w pierwszej kolejności.

Chciałbym usłyszeć sugestie w inny sposób, aby sobie z tym poradzić, ale może powinienem napisać własne pytanie. Chciałem to umieścić w komentarzu, ale okazało się, że jest zbyt długo dla tego ...

tl; dr
Wiązanie danych może powodować problemy z wydajnością na złożonych stronach.

325
Noor A Shuvo 5 listopad 2018, 12:56

To moje podstawowe zrozumienie. Może się mylić!

  1. Przedmioty są oglądane, przekazując funkcję (zwracając rzeczą oglądany) do metody $watch.
  2. Zmiany oglądania elementów muszą być wykonane w bloku kodu Owinięty metodą $apply.
  3. Na końcu metody $apply $digest jest wywoływana przez każdą ze zegarków i sprawdza, czy od tego czasu się zmieniły Ostatni raz pobiegł $digest.
  4. Jeśli pojawi się jakiekolwiek zmiany, strawia jest ponownie wywołana, aż wszystkie zmiany ustabilizują.

W normalnym rozwoju, składnia wiążąca do danych w HTML mówi kompilatorowi AngularJS, aby utworzyć zegarki dla Ciebie, a metody sterownika są już uruchamiane w $apply już. Do programistów aplikacji jest przejrzysty.

82
Merlin 6 styczeń 2017, 08:02

Zastanawiałem się tym na chwilę. Bez osadników, jak zawiadomienie AngularJS zmiany w obiekcie $scope? Czy on je ankiety?

To, co faktycznie robi: każde "normalne" miejsce, które modyfikujesz model, został już wywołany z wnętrzności {x0}}, więc automatycznie wywołuje $apply po uruchomieniu kodu. Powiedz, że Twój kontroler ma metodę, która jest podłączona do ng-click na pewnym elemencie. Ponieważ AngularJS przewody połączenia tej metody razem dla Ciebie, ma szansę wykonać $apply w odpowiednim miejscu. Podobnie, dla wyrażeń, które pojawiają się w widoku, są wykonywane przez AngularJS, więc robi $apply.

Gdy dokumentacja mówi o konieczności wywołania $apply ręcznie do kodu poza AngularJS , mówi o kodzie, który po uruchomieniu nie ma wynika z AngularJS sam w stosie połączeń.

63
Raghav Dinesh 6 maj 2018, 00:43

Wyjaśnienie ze zdjęciami:

Wiązanie danych wymaga mapowania

Odniesienie w zakresie nie jest dokładnie odwołaniem w szablonie. Gdy dane wiążą dwa obiekty, potrzebujesz trzeciego, który słuchasz pierwszego i modyfikować drugi.

enter image description here

Tutaj, gdy zmodyfikujesz <input>, dotkniesz Data-Ref3 . A Klasyczne Mecanizm wiązania danych zmieni Data-Ref4 . Więc jak pojawią się inne wyrażenia {X1}}?

Wydarzenia prowadzą do $ Digest ()

enter image description here

Kątowy utrzymuje oldValue i newValue każdego wiązania. A po każdym wydarzeniu kątowym słynne $digest() pętla sprawdzi listę obserwacyjną, aby sprawdzić, czy coś się zmieniło. Te zdarzenia kątowe ng-click, ng-change, ng-change $http zakończone ... $digest() pętla tak długo, jak każda oldValue różni się z newValue.

W poprzednim obrazie zauważą, że dane-Ref1 i Data-Ref2 zmieniły się.

Wnioski

To trochę jak jajko i kurczak. Nigdy nie wiesz, kto zaczyna się, ale mam nadzieję, że działa przez większość czasu zgodnie z oczekiwaniami.

Innym punktem jest to, że łatwo rozumiesz wpływ głębokości prostego wiązania pamięci i procesora. Mam nadzieję, że komputery stacjonarne są wystarczająco tłuste, aby sobie z tym poradzić. Telefony komórkowe nie są takie silne.

33
Nicolas Zozol 16 wrzesień 2017, 07:36

Oczywiście nie ma okresowej sprawdzania Scope, czy istnieje jakakolwiek zmiana w obiektach dołączona do niego. Nie wszystkie obiekty dołączone do zakresu są oglądane. Zakres prototypucznie utrzymuje obserwatorzy $$ . Scope tylko iteruje przez to $$watchers, kiedy nazywa się $digest.

Kątowy dodaje obserwatora do obserwatorów $$ dla każdego z nich

  1. {{wyrażenie}} - W swoich szablonach (i gdziekolwiek indziej, gdzie jest wyrażenie) lub gdy definiujemy model NG.
  2. $ scope. $ Watch ("Expression / Funkcja") - W JavaScript możemy po prostu dołączyć obiekt Zakresu do oglądania kątowego.

Funkcja $ Watch zajmuje trzy parametry:

  1. pierwszy jest funkcją obserwacyjną, która właśnie zwraca obiekt lub możemy po prostu dodać wyrażenie.

  2. Druga jest funkcją słuchacza, która zostanie wywołana, gdy jest zmiana obiektu. Wszystkie rzeczy takie jak zmiany zostaną wdrożone w tej funkcji.

  3. Trzecim jest opcjonalnym parametrem, który zajmuje logiczną. Jeśli jego prawdziwe, kątowe głębokie ogląda obiekt i wzmacniacz; Jeśli jego fałszywe kątowe po prostu działa odniesienie na obiekcie. Szorstka implementacja $ Watch wygląda taka jak ta

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  
};

Jest ciekawa rzecz w kątowym cyklu strawszym. Cykl $ Digest rozpoczyna się w wyniku połączenia z $ Zakres. $ Digest (). Załóżmy, że zmieniasz model $ Scope w funkcji obsługi przez dyrektywę NG. W takim przypadku AngularJs automatycznie wyzwala cykl $ Digest, dzwoniąc do $ Digest (). Oprócz kliknięcia Ng jest kilka innych wbudowanych dyrektyw / usług, które pozwalają zmienić modele (np. NG-Model, $ limit itp.) i automatycznie wywołać cykl $ strawszy. Szorstka implementacja $ Digest wygląda tak.

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;
 };

Jeśli używamy funkcji Settimeout JavaScript () , aby zaktualizować model zakresu, kanciasty nie ma sposobu, aby wiedzieć, co możesz zmienić. W tym przypadku nasza obowiązek zadzwonić do $ Stosować () ręcznie, który wyzwala cykl $ Digest. Podobnie, jeśli masz dyrektywę, która określa słuchacza zdarzeń Dom i zmienia niektóre modele wewnątrz funkcji obsługi, musisz zadzwonić do $ Zastosuj (), aby zapewnić efekt wprowadzenia zmian. Dużym pomysłem o $ Zastosowanie jest to, że możemy wykonać jakiś kod, który nie jest świadomy kątowego, który może nadal zmienić rzeczy w zakresie. Jeśli opublikujemy ten kod w $ Zastosuj, zadba o dzwonienie do $ Digest (). Szorstka implementacja $ Zastosuj ().

Scope.prototype.$apply = function(expr) {
       try {
         return this.$eval(expr); //Evaluating code in the context of Scope
       } finally {
         this.$digest();
       }
};
22
Merlin 6 styczeń 2017, 08:04

AngularJS Uchwyć mechanizm wiążący do danych za pomocą trzech potężnych funkcji: $ Watch (), $ Digest () i $ Zastosuj (). Większość czasu Angularjs zadzwonią do $ Scope. $ Watch () i $ Scope. $ Digest (), ale W niektórych przypadkach możesz ręcznie zadzwonić do tych funkcji, aby zaktualizować nowe wartości.

$ Watch () : -

Ta funkcja służy do przestrzegania zmian w zmiennej w wysokości $. Akceptuje trzy parametry: wyrażenie, obiekt słuchacza i równości, w którym obiekt słuchacza i równości to parametry opcjonalne.

$ Digest () -

Ta funkcja iteruje wszystkie zegarki w obiekcie $ Scope, i jego obiekty SCOPER $ $
(jeśli ma jakieś). Kiedy $ Digest () Iteruje nad zegarkami sprawdza, czy wartość wyrażenia ma zmieniony. Jeśli wartość się zmieniła, AngularJs wywołuje słuchacza Nowa wartość i stara wartość. Funkcja $ Digest () jest nazywana Ilekroć Angularjs uważa, że jest to konieczne. Na przykład po przycisku Kliknij lub po połączeniu AJAX. Możesz mieć kilka przypadków, w których Angularjs Nie nazywa dla Ciebie funkcji $ Digest (). W takim razie musisz Nazwij to sam.

$ Zastosuj () -

Automatyczne automatycznie aktualizuje tylko te zmiany modelu, które znajdują się wewnątrz kontekstu AngularJS. Gdy zmieniasz się w dowolnym modelu poza kontekstem kątowym (jak imprezy przeglądarki Dom, Settimeout, XHR lub biblioteki stron trzecich), musisz poinformować kątowe zmiany, dzwoniąc ręcznie $ zastosować (). Gdy wywołanie funkcji $ Zastosowanie () wykończenia AngularJs wywołuje się wewnętrznie $ Digest (), więc wszystkie wiązania danych są aktualizowane.

16
Raju 6 listopad 2017, 12:57

Zdarzyło się, że musiałem połączyć model danych osoby z formą, co zrobiłem, było bezpośrednim mapowanie danych z formularzem.

Na przykład, jeśli model miał coś takiego:

$scope.model.people.name

Wkład sterowania formularza:

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

W ten sposób zmodyfikujesz wartość kontrolera obiektu, zostanie to odbudowane automatycznie w widoku.

Przykładem, w którym minęłem model, jest aktualizowany z danych serwera, jest wtedy, gdy poprosisz o kod pocztowy i kod pocztowy na podstawie pisemnego ładowania listę kolonii i miast związanych z tym widoku, a domyślnie ustawić pierwszą wartość z użytkownikiem. I to pracowałem bardzo dobrze, co się stało, jest to, że angularJS} czasami zajmuje kilka sekund, aby odświeżyć model, aby to zrobić, możesz umieścić spinner podczas wyświetlania danych.

7
Merlin 6 styczeń 2017, 08:03
  1. W związku z tym powiązanie danych jest podejściem, w którym wartość jest pobierana z modelu danych i włożona do elementu HTML. Nie ma sposobu na aktualizację modelu z widoku. Jest używany w klasycznych systemach szablonów. Te systemy wiążą dane tylko w jednym kierunku.

  2. Wiązanie danych w aplikacjach kątowych jest automatyczna synchronizacja danych między modelem a widokami.

Wiązanie danych umożliwia traktowanie modelu jako pojedynczej źródła prawdy w aplikacji. Widok jest przedstawieniem modelu przez cały czas. Jeśli model zostanie zmieniony, widok odzwierciedla zmianę i odwrotnie.

6
Shankar Gangadhar 17 czerwiec 2017, 19:28

Angular.js tworzy obserwatora dla każdego modelu tworzymy w widoku. Za każdym razem, gdy model zostanie zmieniony, klasa "Ng-brudna" jest uspokajana do modelu, więc obserwator obserwuje wszystkie modele, które mają klasę "Ng-Dirty" i zaktualizuj swoje wartości w kontrolerze i odwrotnie.

4
dtabuenc 31 październik 2016, 00:30

Wiązanie danych:

Co to jest wiązanie danych?

Za każdym razem, gdy użytkownik zmienia dane w widoku, wystąpi aktualizację tej zmiany w modelu zakresu i Ciseversa.

jak to możliwe?

krótka odpowiedź: Z pomocą cyklu trawienia.

Opis: Kątowy JS ustawia obserwatora w modelu zakresu, który pożaruje funkcję słuchacza, jeśli istnieje zmiana modelu.

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

// kod aktualizacji z nową wartością

});

Więc kiedy i jak nazywa się funkcja obserwatora?

Funkcja obserwatora nazywana jest częścią cyklu Digest.

Cykl Digest nazywany jest automatycznie wywołany jako część kątowej JS wbudowanej w dyrektywach / Usługi, takich jak NG-Model, NG-BIND, $ limit czasu, kliknięcia Ng i inne.

Funkcja cyklu cyklu:

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

I.e {x0}}

Uwaga: $ Zastosuj () jest równy $ rootscope. $ Digest () Oznacza to, że brudne sprawdzanie rozpoczyna się bezpośrednio od korzenia lub góry lub zakresu nadrzędnego do wszystkich dziecięcych punktów w aplikacji Kątowej JS.

Powyższe funkcje działają w przeglądarkach, tj. W przypadku wspomnianych wersji również po prostu upewniając się, że aplikacja jest aplikacją kątową JS, co oznacza, że używasz pliku skryptu Framework AngularJS, do którego wymieniono w znaczniku skryptu.

Dziękuję Ci.

3
Dhana 5 lipiec 2018, 11:38