Jak działa wiązanie danych w AngularJS?

1956

Jak działa wiązanie danych w AngularJSramach?

Nie znalazłem szczegółów technicznych na ich stronie . Mniej lub bardziej jasne jest, jak to działa, gdy dane są propagowane z widoku do modelu. Ale w jaki sposób AngularJS śledzi zmiany właściwości modelu bez ustawień i getterów?

Odkryłem, że istnieją obserwatorzy JavaScript, którzy mogą wykonywać tę pracę. Ale nie są obsługiwane w Internet Explorer 6 i Internet Explorer 7 . Skąd więc AngularJS wie, że zmieniłem na przykład następujące i odzwierciedliłem tę zmianę w widoku?

myobject.myproperty="new value";
Pashec
źródło
10
Należy pamiętać, że od wersji kątowej 1.0.0rc1 musisz określić ng-model-instant ( docs-next.angularjs.org/api/... ), aby moderator był na bieżąco aktualizowany. W przeciwnym razie zostanie zaktualizowany po zdarzeniu rozmycia.
Sotomajor
8
Link Marcello jest najwyraźniej zepsuty, więc tutaj znowu: github.com/mhevery/angular.js/blob/master/docs/content/guide/…
riffraff
6
@orian, ten link jest zły. zaktualizowany do (zakładam) jest taki sam - docs.quarejs.org/guide/databinding
Kevin Meredith
11
Dla tych, którzy wciąż czytają to pytanie, zauważ, że Angular 2.0 bardzo zmienił sposób, w jaki zajmują się wiązaniem danych od Angulara 1.x, aby pracować z komponentami internetowymi i rozwiązać wiele problemów w odpowiedziach poniżej.
sierpnia

Odpowiedzi:

2744

AngularJS zapamiętuje wartość i porównuje ją z poprzednią wartością. To jest podstawowa kontrola brudu. Jeśli nastąpi zmiana wartości, następuje uruchomienie zdarzenia zmiany.

$apply()Metoda, która jest co nazywasz podczas przechodzenia z non-angularjs świata w na świecie angularjs, połączeń $digest(). Podsumowanie to po prostu stara, brudna kontrola. Działa na wszystkich przeglądarkach i jest całkowicie przewidywalny.

Aby skontrolować brudne sprawdzanie (AngularJS) w porównaniu ze zmieniającymi się odbiornikami ( KnockoutJS i Backbone.js ): Chociaż brudne sprawdzanie może wydawać się proste, a nawet nieefektywne (zajmę się tym później), okazuje się, że jest ono poprawne semantycznie przez cały czas, podczas gdy nasłuchiwanie zmian ma wiele dziwnych przypadków narożnych i potrzebuje takich rzeczy, jak śledzenie zależności, aby było to poprawniejsze semantycznie. Śledzenie zależności KnockoutJS to sprytna funkcja dla problemu, którego nie ma AngularJS.

Problemy z detektorami zmian:

  • Składnia jest okropna, ponieważ przeglądarki nie obsługują jej natywnie. Tak, istnieją serwery proxy, ale nie są one poprawne semantycznie we wszystkich przypadkach i oczywiście nie ma serwerów proxy w starych przeglądarkach. Najważniejsze jest to, że brudne sprawdzanie pozwala wykonywać POJO , podczas gdy KnockoutJS i Backbone.js zmuszają cię do dziedziczenia po ich klasach i uzyskiwania dostępu do twoich danych za pośrednictwem akcesorów.
  • Zmień koalescencję. Załóżmy, że masz szereg przedmiotów. Załóżmy, że chcesz dodać elementy do tablicy, gdy zapętlasz się, aby dodać, za każdym razem, gdy dodajesz, uruchamiasz zdarzenia po zmianie, która wyświetla interfejs użytkownika. To bardzo źle wpływa na wydajność. Na koniec chcesz zaktualizować interfejs użytkownika tylko raz. Zdarzenia zmiany są zbyt szczegółowe.
  • Detektory zmian uruchamiają natychmiast seter, co stanowi problem, ponieważ detektor zmian może dalej zmieniać dane, co powoduje uruchomienie większej liczby zdarzeń zmiany. Jest to złe, ponieważ na twoim stosie może wystąpić kilka zdarzeń zmiany jednocześnie. Załóżmy, że masz dwie tablice, które należy synchronizować z jakiegokolwiek powodu. Możesz dodawać tylko do jednego lub drugiego, ale za każdym razem dodajesz zdarzenie zmiany, które teraz ma niespójny obraz świata. Jest to bardzo podobny problem do blokowania wątków, którego JavaScript unika, ponieważ każde wywołanie zwrotne jest wykonywane wyłącznie i do końca. Zmiany zdarzeń przerywają to, ponieważ setery mogą mieć dalekosiężne konsekwencje, które nie są zamierzone i nieoczywiste, co powoduje od nowa problem z wątkiem. Okazuje się, że chcesz opóźnić wykonanie detektora i zagwarantować,

Co z wydajnością?

Może się więc wydawać, że jesteśmy spowolnieni, ponieważ brudne sprawdzanie jest nieskuteczne. Tutaj musimy przyjrzeć się liczbom rzeczywistym, a nie tylko argumentom teoretycznym, ale najpierw zdefiniujmy pewne ograniczenia.

Ludzie to:

  • Powolny - wszystko, co jest szybsze niż 50 ms, jest niedostrzegalne dla ludzi i dlatego można je uznać za „natychmiastowe”.

  • Ograniczony - naprawdę nie można pokazać więcej niż około 2000 informacji na jednej stronie. Wszystko inne to naprawdę zły interfejs użytkownika, a ludzie i tak nie mogą tego przetworzyć.

Prawdziwe pytanie brzmi zatem: ile porównań można wykonać w przeglądarce w ciągu 50 ms? Trudno odpowiedzieć na to pytanie, ponieważ w grę wchodzi wiele czynników, ale tutaj jest przypadek testowy: http://jsperf.com/angularjs-digest/6, który tworzy 10 000 obserwatorów. W nowoczesnej przeglądarce zajmuje to nieco mniej niż 6 ms. W przeglądarce Internet Explorer 8 zajmuje to około 40 ms. Jak widać, nie jest to obecnie problemem nawet w przypadku wolnych przeglądarek. Jest zastrzeżenie: porównania muszą być proste, aby zmieścić się w limicie czasu ... Niestety, zbyt wolno jest dodawać powolne porównanie do AngularJS, więc łatwo jest budować wolne aplikacje, gdy nie wiesz, co robią. Mamy jednak nadzieję, że uzyskamy odpowiedź, dostarczając moduł oprzyrządowania, który pokaże, jakie są powolne porównania.

Okazuje się, że gry wideo i procesory graficzne wykorzystują podejście „brudnego sprawdzania”, szczególnie dlatego, że jest spójne. Tak długo, jak przekraczają częstotliwość odświeżania monitora (zwykle 50-60 Hz lub co 16,6-20 ms), każda nadwyżka wydajności jest marnotrawstwem, więc lepiej jest rysować więcej rzeczy, niż zwiększać FPS.

Misko Hevery
źródło
32
@ Mark - tak, w KO po prostu dodajesz .extend ({przepustnica: 500}), aby poczekać 500 milisekund po ostatnim zdarzeniu zmiany, zanim zaczniesz na nim działać.
Daniel Earwicker 13.03.13
158
Ta cała odpowiedź jest świetna, z wyjątkiem tego, że „dopóki osiągną 50 klatek na sekundę, każda wyższa wydajność jest marnotrawstwem, ponieważ ludzkie oko nie może tego docenić, więc lepiej jest rysować więcej, niż zwiększać liczbę klatek na sekundę”. To stwierdzenie jest całkowicie niepoprawne w zależności od aplikacji. Oko może zdecydowanie docenić ponad 50 fps, a ponieważ różne problemy z pokazem VR (czytaj jeden z najnowszych artykułów Johna Carmacka lub Michaela Abrasha, szczególnie jego przemówienie na GDC 2013 VR), 50 fps jest w rzeczywistości o wiele za wolne. Poza tym twoja odpowiedź jest świetna. Po prostu nie chcę szerzyć dezinformacji.
Nate Bundy,
10
@DavidRivers us jest µs jak utorrent 1µs = 0,000001s
Thorgeir
33
Stwierdzenie to można z łatwością wypowiedzieć na odwrót, ponieważ „Brudne sprawdzanie jest sprytną cechą problemu, którego nie ma nokaut”. ES6 używa obserwowalnych, a kątowe eliminuje brudne sprawdzanie. Świat rzeczywisty dogonił tę odpowiedź i pokazał, że jest fałszywa.
stożkowaty
17
„Coś szybszego niż 50 ms jest niedostrzegalne dla ludzi” nie jest prawdą. W trakcie testów stwierdziliśmy, że nasi klienci mogą łatwo odróżnić opóźnienie aktualizacji 50 ms (20 klatek na sekundę) od opóźnienia aktualizacji 16,6 ms (60 klatek na sekundę). Sceny działające z poprzednią prędkością konsekwentnie pogarszają się w ogólnej ocenie „jak się czuł”, nawet gdy ludzie nie zarejestrowali świadomie liczby klatek na sekundę.
Crashworks
323

Misko przedstawił już doskonały opis działania powiązań danych, ale chciałbym dodać mój pogląd na problem z wydajnością powiązania danych.

Jak stwierdził Misko, około 2000 powiązań zaczyna powodować problemy, ale i tak nie powinno być więcej niż 2000 informacji na stronie. Może to być prawda, ale nie każde powiązanie danych jest widoczne dla użytkownika. Gdy zaczniesz budować dowolny rodzaj widżetu lub siatki danych z dwukierunkowym wiązaniem, możesz łatwo trafić w 2000 powiązań bez złego UX.

Rozważmy na przykład pole kombi, w którym można wpisać tekst, aby przefiltrować dostępne opcje. Ten rodzaj kontroli może zawierać ~ 150 przedmiotów i nadal może być bardzo użyteczny. Jeśli ma jakąś dodatkową funkcję (na przykład określoną klasę w aktualnie wybranej opcji), zaczynasz otrzymywać 3-5 wiązań na opcję. Umieść trzy z tych widżetów na stronie (np. Jeden, aby wybrać kraj, drugi, aby wybrać miasto we wspomnianym kraju, a trzeci, aby wybrać hotel), a już masz około 1000 do 2000 powiązań.

Lub rozważ siatkę danych w korporacyjnej aplikacji internetowej. 50 wierszy na stronie nie jest nierozsądne, z których każdy może mieć 10-20 kolumn. Jeśli zbudujesz to za pomocą powtórzeń ng i / lub masz informacje w niektórych komórkach, które używają niektórych powiązań, możesz zbliżyć się do 2000 powiązań z samą siatką.

Uważam, że jest to ogromny problem podczas pracy z AngularJS, a jedynym rozwiązaniem, jakie udało mi się znaleźć do tej pory, jest konstruowanie widgetów bez użycia dwukierunkowego wiązania, zamiast używania ngOnce, wyrejestrowywania obserwatorów i podobnych sztuczek, lub konstruowania dyrektywy budujące DOM za pomocą jQuery i manipulacji DOM. Wydaje mi się, że w pierwszej kolejności jest to sprzeczne z celem używania Angulara.

Chciałbym usłyszeć sugestie dotyczące innych sposobów rozwiązania tego problemu, ale może powinienem napisać własne pytanie. Chciałem to skomentować, ale okazało się, że jest na to za długo ...

TL; DR
Powiązanie danych może powodować problemy z wydajnością na złożonych stronach.

MW.
źródło
26
Tak, popieram to. Podstawową odpowiedzialnością naszej aplikacji jest wyświetlanie połączeń między różnymi podmiotami. Dana strona może mieć 10 sekcji. Każda sekcja ma tabelę. Każda tabela ma 2-5 typów filtrów przednich. Każda tabela ma 2-5 kolumn, każda z 10 rzędami. Bardzo szybko napotykamy problemy z perfem i korzystamy z opcji „podobnych sztuczek”.
Scott Silvi,
10
Czy można uczciwie powiedzieć, że Angular to nie tylko wiązanie danych, a niektóre aplikacje mogą nie chcieć korzystać z tej funkcji z dokładnie tych powodów, o których wspominali inni? Myślę, że podejście oparte na DI i modułowości jest bardzo warte; posiadanie magicznego automatycznego wiązania jest przyjemne, ale w każdej istniejącej implementacji występuje kompromis wydajności. Sposób Angulara jest prawdopodobnie lepszy w przypadku większości aplikacji internetowych CRUD, a ludzie po prostu uderzają o ścianę, próbując doprowadzić ją do skrajności. Byłoby miło mieć obsługiwaną alternatywną metodę nasłuchiwania zdarzeń, ale może jest to zasadniczo zbyt skomplikowane dla pojedynczego frameworka?
Jason Boyd
8
Angular ma teraz jednokierunkowe i jednorazowe wiązanie danych, które pomaga rozwiązać ten problem. Ponadto ma teraz indeksy dla źródła repeatera, co pozwala modyfikować listę bez przebudowywania domeny dla całej zawartości.
Gaute Løken
6
@MW. Szczerze mówiąc, myślałem, że raz był w centrum. Ale wydaje się, że nie. Jest to po prostu coś, co możesz zrobić, pisząc własne dyrektywy, w zasadzie łącząc różne rzeczy bez ich oglądania. Istnieje jednak modyfikator ux: github.com/pasvaz/bindonce
Gaute Løken
9
Krzyk z przyszłości dla każdego, kto to czyta: kiedyś wiązanie jest teraz kluczową funkcją w Angular v1.3, czytaj więcej tutaj: docs.angularjs.org/guide/expression
Nobita
158

Brudne sprawdzanie $scopeobiektu

Angular utrzymuje prostą arrayobserwację $scopeobiektów. Jeśli sprawdzisz jakiś $scope, okaże się, że zawiera on arraywywołanie $$watchers.

Każdy obserwator objectzawiera między innymi

  1. Wyrażenie monitorowane przez obserwatora. To może być tylko attributenazwa lub coś bardziej skomplikowanego.
  2. Ostatnia znana wartość wyrażenia. Można to porównać z bieżącą obliczoną wartością wyrażenia. Jeśli wartości się różnią, obserwator uruchomi funkcję i oznaczy $scopejako brudny.
  3. Funkcja, która zostanie wykonana, jeśli obserwator jest brudny.

Jak definiowani są obserwatorzy

Istnieje wiele różnych sposobów definiowania obserwatora w AngularJS.

  • Można wyraźnie ty na .$watchattribute$scope

    $scope.$watch('person.username', validateUnique);
  • Możesz umieścić {{}}interpolację w swoim szablonie (dla bieżącego zostanie utworzony obserwator $scope).

    <p>username: {{person.username}}</p>
  • Możesz poprosić o dyrektywę, taką jak ng-modelzdefiniowanie obserwatora dla ciebie.

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

$digestCykl sprawdza wszystkie obserwatorów przed ich ostatniej wartości

Kiedy wchodzimy w interakcję z AngularJS poprzez normalne kanały (model ng, powtórzenie ng itp.), Dyrektywa uruchomi cykl podsumowania.

Cykl trawienia to pierwsze przejście w głąb $scopei wszystkich jego dzieci . Dla każdego z nich $scope objectiterujemy $$watchers arrayi oceniamy wszystkie wyrażenia. Jeśli nowa wartość wyrażenia różni się od ostatniej znanej wartości, wywoływana jest funkcja obserwatora. Ta funkcja może ponownie skompilować część DOM, ponownie obliczyć wartość $scope, wyzwolić AJAX requestdowolne potrzebne zadanie.

Każdy zakres jest przeglądany, a każde wyrażenie obserwowane jest oceniane i sprawdzane względem ostatniej wartości.

Jeśli obserwator zostanie wyzwolony, $scopejest brudny

Jeśli obserwator zostanie wyzwolony, aplikacja wie, że coś się zmieniło i $scopejest oznaczony jako brudny.

Funkcje obserwatora mogą zmieniać inne atrybuty na $scopelub na rodzicu $scope. Jeśli jedna $watcherfunkcja została uruchomiona, nie możemy zagwarantować, że nasze pozostałe $scopesą nadal czyste, dlatego ponownie wykonujemy cały cykl podsumowania.

Wynika to z faktu, że AngularJS ma dwukierunkowe wiązanie, dzięki czemu dane mogą być przekazywane z powrotem do $scopedrzewa. Możemy zmienić wartość wyższą, $scopektóra została już przetworzona. Być może zmienimy wartość na $rootScope.

Jeśli $digestjest brudny, $digestponownie wykonujemy cały cykl

Ciągle zapętlamy $digestcykl, aż albo cykl podsumowania nie stanie się czysty (wszystkie $watchwyrażenia mają taką samą wartość jak w poprzednim cyklu), lub osiągniemy limit podsumowania. Domyślnie limit ten wynosi 10.

Jeśli osiągniemy limit skrótu, AngularJS zgłosi błąd w konsoli:

10 $digest() iterations reached. Aborting!

Podsumowanie jest trudne na komputerze, ale łatwe dla programisty

Jak widać, za każdym razem, gdy coś zmienia się w aplikacji AngularJS, AngularJS sprawdzi każdego obserwatora w $scopehierarchii, aby zobaczyć, jak zareagować. Dla programisty jest to ogromny dar wydajności, ponieważ teraz nie musisz pisać prawie żadnego kodu okablowania, AngularJS po prostu zauważy, czy wartość się zmieniła, i sprawi, że reszta aplikacji będzie zgodna ze zmianą.

Z punktu widzenia maszyny jest to jednak bardzo nieefektywne i spowolni naszą aplikację, jeśli utworzymy zbyt wielu obserwatorów. Misko podało liczbę około 4000 obserwatorów, zanim Twoja aplikacja będzie działać wolniej w starszych przeglądarkach.

Ten limit jest łatwo osiągalny, jeśli na przykład ng-repeatprzekraczasz dużą wartość JSON array. Można temu zaradzić, korzystając z takich funkcji, jak jednorazowe wiązanie w celu skompilowania szablonu bez tworzenia obserwatorów.

Jak uniknąć tworzenia zbyt wielu obserwatorów

Za każdym razem, gdy użytkownik wchodzi w interakcję z aplikacją, każdy obserwator w Twojej aplikacji zostanie oceniony co najmniej raz. Dużą częścią optymalizacji aplikacji AngularJS jest zmniejszenie liczby obserwatorów w twoim $scopedrzewie. Jednym łatwym sposobem na to jest wiązanie jednorazowe .

Jeśli masz dane, które rzadko się zmieniają, możesz je powiązać tylko raz, używając składni ::, tak jak:

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

lub

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

Powiązanie zostanie uruchomione tylko wtedy, gdy szablon zawierający zostanie wyrenderowany i dane zostaną załadowane $scope.

Jest to szczególnie ważne, gdy masz ng-repeatwiele przedmiotów.

<div ng-repeat="person in people track by username">
  {{::person.username}}
</div>
superluminarny
źródło
Dzięki @ user2864740 - choć słusznie odpowiedź Misko powinna być na górze. Zna ramy lepiej niż ktokolwiek inny i jest całkiem fajne, że angażuje się w Stack Overflow ..
superluminary
4
Nie zgadzam się, że wspomniana odpowiedź powinna znajdować się na górze; istnieje różnica między wiedzą o czymś a pisaniem odpowiedniej / szczegółowej odpowiedzi na konkretne pytanie. Są lepsze sposoby na zdobycie wyróżnień. W każdym razie ..
user2864740
1
Nie wątpię, że to prawda, ale pytania i odpowiedzi odpowiedzi :)
user2864740
3
Fajna odpowiedź obejmująca zachowanie brudnego czeku i jego ocenę, jedna rzecz nie była zbyt jasna w odpowiedzi Misko.
strider
3
Znakomita i szczegółowa odpowiedź. @ superluminary, dziękuję za taką odpowiedź. Co więcej, po przeczytaniu tej odpowiedzi dochodzę do tego, że nie wolno nam dodawać wyrażenia niebędącego idempotentem jako obserwowanego wyrażenia.
Mangu Singh Rajpurohit
81

To jest moje podstawowe zrozumienie. To może być źle!

  1. Elementy są obserwowane poprzez przekazanie funkcji (zwracanie rzeczy do obejrzenia) do $watchmetody.
  2. Zmiany w obserwowanych elementach należy wprowadzać w bloku kodu zawiniętym $applymetodą
  3. Pod koniec $applytej $digestmetoda jest wywoływana, który przechodzi każdy z zegarków i sprawdza, czy one zmianie od ostatniej chwili $digestuciekł.
  4. Jeśli zostaną znalezione jakieś zmiany, podsumowanie jest wywoływane ponownie, aż wszystkie zmiany ustabilizują się.

W normalnym rozwoju składnia powiązania danych w HTML mówi kompilatorowi AngularJS, aby utworzył dla ciebie zegarki, a metody kontrolera są $applyjuż uruchomione . Dla programisty aplikacji wszystko jest przejrzyste.

Pete BD
źródło
4
kiedy uruchamiana jest metoda zastosuj?
numan salati
3
@EliseuMonar Pętla podsumowania działa w wyniku jakiegoś zdarzenia lub wywołania $ apply (), nie jest wywoływana okresowo na podstawie timera. zobacz Jak działa funkcja $ Watch w AngularJS? i jak działa wiązanie i trawienie w AngularJS?
adl
1
@remi, nie martwię się o ostatnią wersję AngularJS. Czy już używają serwerów proxy lub Object.observe? Jeśli nie, nadal znajdują się w erze brudnego sprawdzania, która tworzy pętlę czasową, aby sprawdzić, czy atrybuty modelu uległy zmianie.
Eliseu Monar dos Santos
1
przeczytałem, że skrót będzie działał maksymalnie dziesięć razy sitepoint.com/understanding-angulars-apply-digest
użytkownik137717
62

Zastanawiałem się przez chwilę. Bez setterów jak AngularJSzauważają zmiany w $scopeobiekcie? Czy to sonduje je?

W rzeczywistości robi to: każde „normalne” miejsce, w którym modyfikujesz model, było już wywoływane z trzewi AngularJS, więc automatycznie wywołuje $applycię po uruchomieniu kodu. Powiedzmy, że twój kontroler ma metodę, która jest podłączona do ng-clickjakiegoś elementu. Ponieważ AngularJSłączysz wywoływanie tej metody razem, masz szansę zrobić to $applyw odpowiednim miejscu. Podobnie, dla wyrażeń pojawiających się bezpośrednio w widokach, są one wykonywane przez, AngularJSwięc robi to $apply.

Kiedy dokumentacja mówi o konieczności $applyręcznego wywoływania kodu pozaAngularJS , mówi o kodzie, który po uruchomieniu nie wynika z AngularJSsamego siebie na stosie wywołań.

jpsimons
źródło
32

Objaśnienie ze zdjęciami:

Powiązanie danych wymaga mapowania

Odwołanie w zakresie nie jest dokładnie odwołaniem w szablonie. Podczas wiązania dwóch obiektów potrzebny jest trzeci, który nasłuchuje pierwszego i zmodyfikuje drugi.

wprowadź opis zdjęcia tutaj

Tutaj, po zmodyfikowaniu <input>, dotkniesz data-ref3 . A klasyczny mechanizm wiązania danych zmieni dane ref4 . Jak poruszą się inne {{data}}wyrażenia?

Zdarzenia prowadzą do $ digest ()

wprowadź opis zdjęcia tutaj

Kątowe utrzymuje oldValuei newValueod każdego wiążące. I po każdym wydarzeniu$digest() Angulara słynna pętla sprawdzi WatchList, aby sprawdzić, czy coś się zmieniło. Te kątowe zdarzeniang-click, ng-change, $httpzakończone ... The $digest()pętla będzie tak długo, jak każdy oldValueróżni się od newValue.

Na poprzednim zdjęciu zauważysz, że data-ref1 i data-ref2 uległy zmianie.

Wnioski

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

Drugą kwestią jest to, że można łatwo zrozumieć głębokie oddziaływanie prostego wiązania na pamięć i procesor. Mam nadzieję, że komputery stacjonarne są wystarczająco grube, aby sobie z tym poradzić. Telefony komórkowe nie są tak silne.

Nicolas Zozol
źródło
22

Oczywiście nie ma okresowej kontroli, Scopeczy istnieje jakaś zmiana w dołączonych do niej obiektach. Nie wszystkie obiekty dołączone do lunety są obserwowane. Zakres prototypowo utrzymuje obserwatorów $$ . Scopetylko iteruje przez to, $$watcherskiedy $digestjest wywoływany.

Angular dodaje obserwatora do obserwatorów $$ dla każdego z nich

  1. {{expression}} - W twoich szablonach (i gdziekolwiek indziej, gdzie jest wyrażenie) lub gdy definiujemy ng-model.
  2. $ scope. $ watch ('expression / function') - W twoim JavaScript możemy po prostu dołączyć obiekt zakresu do obserwacji kątowej.

Funkcja $ watch przyjmuje trzy parametry:

  1. Pierwszy to funkcja obserwatora, która po prostu zwraca obiekt lub możemy po prostu dodać wyrażenie.

  2. Drugi to funkcja detektora, która zostanie wywołana, gdy nastąpi zmiana w obiekcie. Wszystkie rzeczy takie jak zmiany DOM zostaną zaimplementowane w tej funkcji.

  3. Trzeci jest parametrem opcjonalnym, który przyjmuje wartość logiczną. Jeśli to prawda, kątowe głębokie obserwuje obiekt, a jeśli jego fałszywe kątowe po prostu ogląda obiekt referencyjny. Rough Implementacja $ watch wygląda następująco

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

W Angular jest interesująca rzecz o nazwie Cykl trawienny. Cykl $ digest rozpoczyna się w wyniku wywołania $ scope. $ Digest (). Załóżmy, że zmieniasz model $ scope w funkcji modułu obsługi poprzez dyrektywę ng-click. W takim przypadku AngularJS automatycznie uruchamia cykl $ digest przez wywołanie $ digest (). Oprócz ng-click istnieje kilka innych wbudowanych dyrektyw / usług, które pozwalają zmieniać modele (np. Ng-model, $ limit czasu itp.) i automatycznie uruchamia cykl $ streszczenia. Z grubsza implementacja $ digest wygląda następująco.

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żyjemy funkcji setTimeout () JavaScript do aktualizacji modelu zakresu, Angular nie będzie wiedział, co możesz zmienić. W takim przypadku naszym obowiązkiem jest ręczne wywołanie $ Apply (), co uruchomi cykl skrótu $. Podobnie, jeśli masz dyrektywę, która konfiguruje detektor zdarzeń DOM i zmienia niektóre modele wewnątrz funkcji modułu obsługi, musisz wywołać $ apply (), aby zmiany zostały wprowadzone. Główną ideą $ Apply jest to, że możemy wykonać kod, który nie jest świadomy Angulara, a kod ten może wciąż zmieniać rzeczy w zakresie. Jeśli zawiniemy ten kod w $ apply, zajmie się wywołaniem $ digest (). Szorstka implementacja $ apply ().

Scope.prototype.$apply = function(expr) {
       try {
         return this.$eval(expr); //Evaluating code in the context of Scope
       } finally {
         this.$digest();
       }
};
Sasank Sunkavalli
źródło
15

AngularJS obsługuje mechanizm wiązania danych za pomocą trzech potężnych funkcji: $ watch () , $ digest () i $ apply () . Przez większość czasu AngularJS wywołuje $ scope. $ Watch () i $ scope. $ Digest (), ale w niektórych przypadkach może być konieczne ręczne wywołanie tych funkcji w celu zaktualizowania o nowe wartości.

$ watch () : -

Ta funkcja służy do obserwowania zmian w zmiennej w zakresie $. Akceptuje trzy parametry: wyrażenie, obiekt nasłuchujący i obiekt równości, gdzie obiekt nasłuchiwania i obiekt równości są parametrami opcjonalnymi.

$ digest () -

Ta funkcja wykonuje iterację przez wszystkie zegarki w obiekcie $ scope i jego podrzędnych obiektach $ scope
(jeśli posiada). Gdy $ digest () przechodzi przez zegarki, sprawdza, czy wartość wyrażenia uległa zmianie. Jeśli wartość się zmieniła, AngularJS wywołuje słuchacza z nową wartością i starą wartością. Funkcja $ digest () jest wywoływana za każdym razem, gdy AngularJS uzna to za konieczne. Na przykład po kliknięciu przycisku lub po wywołaniu AJAX. Możesz mieć przypadki, w których AngularJS nie wywołuje dla ciebie funkcji $ digest (). W takim przypadku musisz to nazwać samemu.

Zastosuj $ () -

Angular do automatycznie aktualizuje tylko te zmiany modelu, które są w kontekście AngularJS. Jeśli zmienisz w dowolnym modelu poza kontekstem Angular (np. Zdarzenia DOM przeglądarki, setTimeout, XHR lub biblioteki stron trzecich), musisz poinformować Angular o zmianach, ręcznie wywołując $ Apply (). Kiedy wywołanie funkcji $ Apply () zakończy się, AngularJS wywołuje $ digest () wewnętrznie, więc wszystkie powiązania danych są aktualizowane.

Bharath Kumar
źródło
7

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

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

$scope.model.people.name

Kontrolne wejście formularza:

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

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

Przykładem, w którym zdałem, że model jest aktualizowany z danych serwera, jest prośba o kod pocztowy i kod pocztowy oparty na zapisanych obciążeniach, listę kolonii i miast powiązanych z tym widokiem i domyślnie ustawia pierwszą wartość dla użytkownika. I to działało bardzo dobrze, co się dzieje, że angularJSczasami odświeżenie modelu zajmuje kilka sekund, aby to zrobić, możesz umieścić pokrętło podczas wyświetlania danych.

gartox
źródło
14
Przeczytałem tę odpowiedź 5 razy i nadal nie rozumiem, co tu oznacza.
sbedulin
1
Odpowiedź wydaje mi się zagadką
Aman
6
  1. Powiązanie danych w jedną stronę to podejście, w którym wartość jest pobierana z modelu danych i wstawiana do elementu HTML. Nie ma możliwości zaktualizowania modelu z widoku. Jest stosowany w klasycznych systemach szablonów. Systemy te wiążą dane tylko w jednym kierunku.

  2. Powiązanie danych w aplikacjach Angular to automatyczna synchronizacja danych między modelem a komponentami widoku.

Powiązanie danych pozwala traktować model jako jedyne źródło prawdy w aplikacji. Widok jest zawsze rzutem modelu. Jeśli model zostanie zmieniony, widok odzwierciedla zmianę i odwrotnie.

Shankar Gangadhar
źródło
5

Oto przykład powiązania danych z AngularJS przy użyciu pola wejściowego. Wyjaśnię później

Kod HTML

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

Kod AngularJS

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

Jak widać w powyższym przykładzie, AngularJS używa ng-modeldo nasłuchiwania i obserwowania, co dzieje się z elementami HTML, zwłaszcza na inputpolach. Kiedy coś się stanie, zrób coś. W naszym przypadku ng-modelwiąże się z naszym widokiem przy użyciu zapisu wąsów {{}}. Cokolwiek wpisane jest w polu wprowadzania, jest natychmiast wyświetlane na ekranie. Na tym polega piękno wiązania danych przy użyciu AngularJS w najprostszej formie.

Mam nadzieję że to pomoże.

Zobacz działający przykład tutaj na Codepen

AllJs
źródło
5

AngularJs obsługuje dwukierunkowe wiązanie danych .
Oznacza dostęp do danych Widok -> Kontroler i kontroler -> Widok

Dla np.

1)

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

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

O / P

Peter

Możesz powiązać dane w ng-modelpolubieniu: -
2)

<input ng-model="name" />

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

Tutaj w powyższym przykładzie, cokolwiek poda użytkownik, będzie widoczne w <div>znaczniku.

Jeśli chcesz powiązać dane wejściowe z HTML do kontrolera: -
3)

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

Tutaj, jeśli chcesz użyć danych wejściowych namew kontrolerze,

$scope.name = {};

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

ng-modelwiąże nasz pogląd i wyraża go w wyrażeniu {{ }}.
ng-modelto dane pokazane użytkownikowi w widoku i z którymi użytkownik wchodzi w interakcje.
Więc łatwo jest powiązać dane w AngularJs.

ojus kulkarni
źródło
4

Angular.js tworzy obserwator dla każdego modelu, który tworzymy w widoku. Za każdym razem, gdy model jest zmieniany, do modelu dołączana jest klasa „brudny”, więc obserwator obserwuje wszystkie modele, które mają klasę „brudny” i aktualizuje ich wartości w kontrolerze i odwrotnie.

Shankar Gangadhar
źródło
3

wiązanie danych:

Co wiąże dane?

Za każdym razem, gdy użytkownik zmienia dane w widoku, następuje aktualizacja tej zmiany w modelu zakresu i odwrotnie.

Jak to jest możliwe?

Krótka odpowiedź: za pomocą cyklu trawienia.

Opis: Angular js ustawia obserwatora na modelu zakresu, który uruchamia funkcję detektora, jeśli nastąpi zmiana w modelu.

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

// Kod aktualizacji Dom z nową wartością

});

Kiedy i jak wywoływana jest funkcja obserwatora?

Funkcja obserwatora jest wywoływana jako część cyklu podsumowania.

Cykl trawienia jest wywoływany automatycznie jako część kątowych js wbudowanych w dyrektywy / usługi, takie jak ng-model, ng-bind, $ timeout, ng-click i inne .., które pozwalają uruchomić cykl podsumowania.

Funkcja cyklu trawienia:

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

to znaczy$rootScope.$apply()

Uwaga: $ Apply () jest równe $ rootScope. $ Digest () oznacza, że ​​brudne sprawdzanie rozpoczyna się od katalogu głównego lub góry lub zakresu nadrzędnego aż do wszystkich potomnych zakresów $ w aplikacji kątowej js.

Powyższe funkcje działają w przeglądarkach IE dla wspomnianych wersji także po prostu upewniając się, że twoja aplikacja jest aplikacją angs js, co oznacza, że ​​używasz pliku skryptu frameworku angularjs, do którego odwołuje się znacznik script.

Dziękuję Ci.

Dhana
źródło