AngularJS: Різниця між методами $ спостерігати і $ watch


378

Я знаю, що і те, Watchersі Observersобчислюється, як тільки щось $scopeзміниться в AngularJS. Але я не міг зрозуміти, у чому саме різниця між ними.

Моє первісне розуміння полягає в тому Observers, що обчислюються для кутових виразів, які є умовами на стороні HTML, де Watchersвиконано, коли виконується $scope.$watch()функція. Я правильно думаю?


1
Ваша редакція не є корисною і трохи не викликає труднощів. Будьте уважні до інших, які приходять сюди за фактичною допомогою.
smalone

@smalone змінено. Дякую і вибачте!
Абілаш

👍 Не хвилюйтесь. Дякуємо за виправлення.
smalone

Відповіді:


608

$ obser () - метод наоб'єкті Attributes , і як такий він може використовуватися лише для спостереження / перегляду зміни значення атрибута DOM. Він застосовується / називається всередині директив. Використовуйте $ obser, коли вам потрібно спостерігати / переглядати атрибут DOM, який містить інтерполяцію (тобто {{}} 's).
Наприклад,attr1="Name: {{name}}", то в директиві:attrs.$observe('attr1', ...).
(Якщо ви спробуєте,scope.$watch(attrs.attr1, ...)це не спрацює через {{}} s - ви отримаєтеundefined.) Використовуйте $ watch для всього іншого.

$ watch () складніше. Він може спостерігати / спостерігати за «виразом», де вираз може бути або функцією, або рядком. Якщо вираз є рядком, це $ parse 'd (тобто оцінюється як кутовий вираз ) у функцію. (Саме ця функція називається кожним циклом дайджесту.) Виразний рядок не може містити {{}} 's. $ watch - це метод наоб'єкті Scope , тому його можна використовувати / викликати, де б ви не мали доступу до об'єкта області, отже, у

  • контролер - будь-який контролер - один, створений через ng-view, ng-контролер або контролер
  • функція зв'язування в директиві, оскільки вона має доступ і до сфери застосування

Оскільки рядки оцінюються як кутові вирази, $ watch часто використовується, коли потрібно спостерігати / дивитися властивість моделі / області застосування. Наприклад, attr1="myModel.some_prop"у функції контролера або зв'язку: scope.$watch('myModel.some_prop', ...)або scope.$watch(attrs.attr1, ...)(або scope.$watch(attrs['attr1'], ...)).
(Якщо ви спробуєте, attrs.$observe('attr1')ви отримаєте рядок myModel.some_prop, який, мабуть, не є тим, що ви хочете.)

Як було обговорено в коментарях до відповіді @ PrimosK, всі $ спостереження та $ годинники перевіряються кожен цикл дайджесту .

Директиви з ізоляційними сферами складніші. Якщо використовується синтаксис '@', ви можете $ спостерігати або $ спостерігати атрибут DOM, який містить інтерполяцію (тобто {{}}). (Причина, яка працює з $ watch, полягає в тому, що синтаксис '@' робить інтерполяцію для нас, отже, $ watch бачить рядок без {{}}). $ дотримуйтесь і для цього випадку.

Щоб допомогти перевірити все це, я написав Plunker, який визначає дві директиви. Один ( d1) не створює нового розмаху, інший ( d2) створює область ізоляції. Кожна директива має однакові шість атрибутів. Кожен атрибут є як $ obser'd, так і $ watch'ed.

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

Подивіться на журнал консолі, щоб побачити відмінності між $ observer і $ watch у функції зв’язку. Потім перейдіть за посиланням і перегляньте, які $ спостереження та $ годинник викликані змінами властивості, здійсненими обробником кліків.

Зауважте, що при запуску функції посилання будь-які атрибути, які містять {{}}, ще не оцінені (тому, якщо ви спробуєте вивчити атрибути, ви отримаєте undefined). Єдиний спосіб побачити інтерпольовані значення - це використовувати $ obser (або $ watch, якщо використовується область ізоляції з '@'). Тому отримання значень цих атрибутів є асинхронною операцією. (І саме тому нам потрібні функції $ watch і $ watch.)

Іноді вам не потрібно $ спостерігати або $ watch. Наприклад, якщо ваш атрибут містить номер або логічне значення ( а не рядки), просто оцінити його одного разу: attr1="22", то, скажімо, в вашій зв'язує функції: var count = scope.$eval(attrs.attr1). Якщо це просто постійний рядок - attr1="my string"- тоді просто використовуйте attrs.attr1у своїй директиві (не потрібно $ eval ()).

Дивіться також публікацію в гуртовій групі Vojta про вирази $ watch.


13
Чудове пояснення! +1
PrimosK

4
Чудова відповідь! Чи маєте ви уявлення, навіщо ng-src/ng-hrefвикористовувати attr.$observeзамість scope.$watchцього?
окм

4
+1 Для Папи AngularJS! Кожен раз, коли я шукаю стек для отримання деякої інформації про мою останню кутову проблему, я неминуче закінчуюся читанням @MarkRajcok прийнятої відповіді.
GFoley83

1
Дякую за чудовий пост. $ eval (пункт) дуже корисний. Якщо елемент є рядком json, він перетворюється на об’єкт json.
bnguyen82

5
@tamakisquare, вони взаємозамінні при використанні @синтаксису. Я вважаю, що різниці в продуктивності немає (але я не переглянув фактичний вихідний код).
Марк Райкок

25

Якщо я правильно розумію ваше запитання, ви запитуєте, в чому різниця, якщо ви реєструєте зворотний дзвінок слухача $watchабо якщо ви це робите $observe.

Зворотний виклик, на який зареєстровано $watch, запускається при $digestвиконанні.

Зворотний виклик, зареєстрований у $observe, викликається при зміні значення атрибутів, що містять інтерполяцію (наприклад attr="{{notJetInterpolated}}").


Всередині директиви ви можете використовувати їх обох дуже схожим чином:

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

або

    scope.$watch(attrs['attrYouWatch'], function() {
         // body
    });

3
Насправді, оскільки кожна зміна відображається у $digestфазі, можна впевнено припустити, що $observeзворотний виклик буде викликаний $digest. І $watchзворотний виклик також буде викликаний , $digestале всякий раз , коли змінюється значення. Я думаю, що вони виконують абсолютно таку ж роботу: "дивіться вираз, викликайте зворотний дзвінок, значення змінюється". Різниця ключових слів, можливо, просто синтаксичний цукор, щоб не заплутати розробника.
Umur Kontacı

1
@fastreload, я повністю згоден з вашим коментарем .. Чудово написано!
PrimosK

@fastreload ... Дякую за чудове пояснення. Якщо я правильно зрозумів, спостерігачі - це кутові вирази. Чи правий я?
Абілаш

@PrimosK: додаю вас до мого попереднього коментаря.
Абілаш

2
@Abilash спостерігачі призначені для перегляду атрибутів dom, а не лише виразів. Отже, якщо ви самі зміните значення атрибуту, це відобразиться в наступному циклі дайджесту.
Umur Kontacı

1

Я думаю, що це досить очевидно:

  • $ obser використовується при пов'язуванні функції директив.
  • $ watch використовується в масштабі, щоб спостерігати за будь-якими змінами його значень.

Майте на увазі : обидві функції мають два аргументи,

$observe/$watch(value : string, callback : function);
  • value : це завжди рядкове посилання на елемент, що спостерігається (ім'я змінної області чи назва атрибута директиви, який слід переглядати)
  • зворотний виклик : функція, яку слід виконати у форміfunction (oldValue, newValue)

Я зробив це plunker, так що ви можете зрозуміти як їх використання. Я використав аналогію Хамелеона, щоб полегшити малювання.


2
Це досить очевидно щодо його використання. Але чому це було питання. Марк це красиво підсумував.
Абілаш

3
Я думаю, що парами можуть бути переключені - це, здається, передає newValue, потім oldValue до attrs. $ Obser (). . .
бластер

0

Чому $ наблюдение відрізняється від $ watch?

Експресія watchExpression оцінюється та порівнюється з попереднім значенням кожного циклу дайджесту (), якщо є зміна значення watchExpression, викликається функція watch.

$ obser є специфічним для перегляду інтерпольованих значень. Якщо значення атрибута директиви є інтерпольованим, наприклад dir-attr="{{ scopeVar }}", функція спостереження буде викликана лише тоді, коли встановлено інтерпольоване значення (а отже, коли $ digest вже визначив, потрібно робити оновлення). По суті, вже є спостерігач за інтерполяцією, і $ obser функція відкидає від цього.

Див. $ Obser & $ set у compile.js

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.