AngularJS: Różnica między metodami obserwacji i obserwacji

378

Wiem o tym Watchersi Observerssą obliczane, gdy tylko coś się $scopezmieni w AngularJS. Ale nie mogłem zrozumieć, jaka dokładnie jest różnica między nimi.

Moje początkowe zrozumienie polega na tym, że Observerssą obliczane dla wyrażeń kątowych, które są warunkami po stronie HTML, gdzie są Watcherswykonywane, gdy $scope.$watch()funkcja jest wykonywana. Czy właściwie myślę?

Abilash
źródło
1
Twoja edycja nie jest pomocna i trochę antagonizuje. Prosimy o wzięcie pod uwagę innych osób przybywających tutaj w celu uzyskania rzeczywistej pomocy.
smalone
@smalone się zmieniło. Dziękuję i przepraszam!
Abilash,
👍 Nie martw się. Dzięki za naprawę.
smalone

Odpowiedzi:

608

$ observ () jest metodą naobiekcie Attributes i jako taka może być używana tylko do obserwowania / obserwowania zmiany wartości atrybutu DOM. Jest używany / nazywany tylko wewnątrz dyrektyw. Użyj $ observ, gdy musisz obserwować / obserwować atrybut DOM zawierający interpolację (tj. {{}}).
Na przykładattr1="Name: {{name}}", a następnie w dyrektywie:attrs.$observe('attr1', ...).
(Jeśli spróbujesz,scope.$watch(attrs.attr1, ...)to nie zadziała z powodu {{}} s - dostanieszundefined.) Użyj $ watch do wszystkiego innego.

$ watch () jest bardziej skomplikowane. Może obserwować / oglądać „wyrażenie”, gdzie wyrażenie może być funkcją lub łańcuchem. Jeśli wyrażenie jest łańcuchem, jest ono $ parsowane 'd (tzn. Oceniane jako wyrażenie kątowe ) na funkcję. (Ta funkcja jest nazywana każdym cyklem podsumowania). Wyrażenie ciągu nie może zawierać {{}}. $ watch jest metodą naobiekcie Scope , więc można jej używać / wywoływać wszędzie tam, gdzie masz dostęp do obiektu zakresu, stąd

  • kontroler - dowolny kontroler - stworzony przez ng-view, ng-kontroler lub kontroler dyrektywy
  • funkcja łączenia w dyrektywie, ponieważ ma ona również dostęp do zakresu

Ponieważ łańcuchy są oceniane jako wyrażenia kątowe, $ watch jest często używany, gdy chcesz obserwować / oglądać właściwość model / scope. Np. attr1="myModel.some_prop"W funkcji kontrolera lub łącza: scope.$watch('myModel.some_prop', ...)lub scope.$watch(attrs.attr1, ...)(lub scope.$watch(attrs['attr1'], ...)).
(Jeśli spróbujesz attrs.$observe('attr1'), otrzymasz ciąg myModel.some_prop, który prawdopodobnie nie jest tym, czego chcesz.)

Jak omówiono w komentarzach do odpowiedzi @ PrimosK, wszystkie $ obserwacje i $ zegarki są sprawdzane w każdym cyklu podsumowania .

Dyrektywy o zakresach izolowanych są bardziej skomplikowane. Jeśli używana jest składnia „@”, możesz obserwować lub obserwować atrybut DOM zawierający interpolację (tj. {{}}). (Powodem, dla którego działa z $ watch, jest to, że składnia „@” wykonuje dla nas interpolację , dlatego $ watch widzi ciąg bez znaków {{}}.) Aby ułatwić zapamiętanie, kiedy użyć, sugeruję użycie Obserwuj również w tym przypadku.

Aby pomóc to wszystko przetestować, napisałem Plunkera, który definiuje dwie dyrektywy. Jeden ( d1) nie tworzy nowego zakresu, drugi ( d2) tworzy zakres izolowany. Każda dyrektywa ma te same sześć atrybutów. Każdy atrybut jest zarówno obserwowany, jak i obserwowany.

<div d1 attr1="{{prop1}}-test" attr2="prop2" attr3="33" attr4="'a_string'"
        attr5="a_string" attr6="{{1+aNumber}}"></div>

Przejrzyj dziennik konsoli, aby zobaczyć różnice między $ observ i $ watch w funkcji łączenia. Następnie kliknij link i sprawdź, które $ obserwacje i $ zegarki są wywoływane przez zmiany właściwości wprowadzone przez moduł obsługi kliknięć.

Zauważ, że po uruchomieniu funkcji link wszelkie atrybuty zawierające {{}} nie są jeszcze oceniane (więc jeśli spróbujesz zbadać atrybuty, otrzymasz undefined). Jedynym sposobem, aby zobaczyć interpolowane wartości, jest użycie $ observ (lub $ watch, jeśli używasz zakresu izolowanego z '@'). Dlatego uzyskanie wartości tych atrybutów jest operacją asynchroniczną . (I dlatego potrzebujemy funkcji $ observ i $ watch.)

Czasami nie potrzebujesz obserwować ani oglądać. Na przykład, jeśli atrybut zawiera liczbę lub wartość logiczną (nie łańcuch), tylko jeden raz ocenić to: attr1="22", a potem, powiedzmy, w swojej funkcji łączącej: var count = scope.$eval(attrs.attr1). Jeśli jest to ciąg stały - attr1="my string"- po prostu użyj attrs.attr1w swojej dyrektywie (nie potrzebujesz $ eval ()).

Zobacz także post grupy google Vojty na temat wyrażeń $ watch.

Mark Rajcok
źródło
13
Świetne wyjaśnienie! +1
PrimosK
4
Świetna odpowiedź! Czy masz pojęcie, dlaczego zamiast tego ng-src/ng-hrefużywać ? attr.$observescope.$watch
okm
4
+1 dla papieża AngularJS! Za każdym razem, gdy szukam w stosie informacji o moim najnowszym problemie z Angularem, nieuchronnie kończę czytanie @MarkRajcok zaakceptował odpowiedź.
GFoley83
1
Dzięki za świetny post. scope. $ eval (item) jest naprawdę pomocny. Jeśli element jest łańcuchem json, jest konwertowany na obiekt json.
bnguyen82,
5
@tamakisquare, są one wymienne podczas korzystania ze @składni. Uważam, że nie ma różnicy w wydajności (ale nie spojrzałem na rzeczywisty kod źródłowy).
Mark Rajcok
25

Jeśli dobrze rozumiem twoje pytanie, pytasz, jaka jest różnica, jeśli zarejestrujesz oddzwonienie do słuchacza $watchlub jeśli to zrobisz $observe.

Oddzwanianie zarejestrowane za pomocą $watchjest uruchamiane, gdy $digestjest wykonywane.

Zarejestrowane wywołanie zwrotne $observejest wywoływane, gdy zmiany wartości atrybutów zawierających interpolację (np attr="{{notJetInterpolated}}".).


Wewnątrz dyrektywy możesz używać obu z nich w bardzo podobny sposób:

    attrs.$observe('attrYouWatch', function() {
         // body
    });

lub

    scope.$watch(attrs['attrYouWatch'], function() {
         // body
    });
PrimosK
źródło
3
W rzeczywistości, ponieważ każda zmiana jest odzwierciedlana w $digestfazie, można bezpiecznie założyć, że $observewywołanie zwrotne zostanie wywołane $digest. I $watchzwrotna zostanie również wezwany $digestale ilekroć wartość ulega zmianie. Myślę, że wykonują dokładnie to samo zadanie: „obserwuj wyrażenie, oddzwoń zmiany wartości”. Różnica słów kluczowych to prawdopodobnie tylko cukier składniowy, aby nie mylić dewelopera.
Umur Kontacı
1
@fastreload, całkowicie zgadzam się z twoim komentarzem .. Ładnie napisane!
PrimosK
@fastreload ... Dzięki za wspaniałe wyjaśnienie. Jeśli dobrze zrozumiałem, obserwatorzy są dla wyrażeń kątowych. Czy mam rację?
Abilash
@PrimosK: dodając cię do mojego poprzedniego komentarza.
Abilash
2
Obserwatorzy @Abilash służą do oglądania atrybutów dom, a nie tylko wyrażeń. Więc jeśli sam zmienisz wartość atrybutu, zostanie ona odzwierciedlona w następnym cyklu podsumowania.
Umur Kontacı
1

Myślę, że to dość oczywiste:

  • $ observ jest używany w funkcji łączenia dyrektyw.
  • $ watch służy do obserwowania zmian wartości.

Pamiętaj : obie funkcje mają dwa argumenty,

$observe/$watch(value : string, callback : function);
  • wartość : jest zawsze ciągiem odniesienia do obserwowanego elementu (nazwa zmiennej zasięgu lub nazwa atrybutu dyrektywy, który ma być obserwowany)
  • callback : funkcja do wykonania z formularzafunction (oldValue, newValue)

Zrobiłem plunker, więc możesz naprawdę zrozumieć oba sposoby ich wykorzystania. Użyłem analogii Chameleon, aby ułatwić sobie wyobrażenie.

Vdegenne
źródło
2
Jest dość oczywisty w zakresie jego zastosowań. Ale dlaczego było to pytanie? Mark pięknie to podsumował.
Abilash
3
Myślę, że parametry mogą być przełączane - wydaje się, że przekazuje newValue, a następnie oldValue do attrs. $ Observ (). . .
Blaster
0

Dlaczego $ observ różni się od $ watch?

Funkcja watchExpression jest oceniana i porównywana z poprzednią wartością w każdym cyklu digest (), jeśli nastąpi zmiana wartości watchExpression, wywoływana jest funkcja watch.

$ observ jest specyficzny dla obserwowania wartości interpolowanych. Jeśli wartość atrybutu dyrektywy jest interpolowana, np. dir-attr="{{ scopeVar }}"Funkcja obserwacyjna będzie wywoływana tylko wtedy, gdy ustawiona jest wartość interpolowana (a zatem, gdy $ digest określił już aktualizacje, należy dokonać aktualizacji). Zasadniczo istnieje już obserwator interpolacji, a funkcja obserwowania $ piggyback poza tym.

Zobacz $ observ & $ set w pliku compile.js

Niko
źródło