Как использовать $ scope. $ Watch и $ scope. $ Apply в AngularJS?

Я не понимаю, как использовать $scope.$watch и $scope.$apply. Официальная документация бесполезна.

Чего я конкретно не понимаю:

  • Они подключены к DOM?
  • Как я могу обновить изменения модели DOM?
  • Какая точка связи между ними?

Я попробовал это руководство, но для этого требуется понимание $watch и $apply как должное.

Что делают $apply и $watch и как их правильно использовать?


person ilyo    schedule 27.02.2013    source источник


Ответы (6)


Вам нужно знать, как работает AngularJS, чтобы понять это.

Цикл дайджеста и $ scope

Прежде всего, AngularJS определяет концепцию так называемого цикла дайджеста. Этот цикл можно рассматривать как цикл, во время которого AngularJS проверяет, есть ли какие-либо изменения для всех переменных, наблюдаемых всеми $scopes. Итак, если у вас есть $scope.myVar, определенный в вашем контроллере, и эта переменная была помечена для наблюдения, то вы неявно указываете AngularJS отслеживать изменения в myVar на каждой итерации цикла.

Естественным последующим вопросом будет: Наблюдается ли все, что связано с $scope? К счастью, нет. Если вы будете следить за изменениями каждого объекта в вашем $scope, то быстро цикл дайджеста займет много времени, чтобы оценить, и вы быстро столкнетесь с проблемами производительности. Вот почему команда AngularJS дала нам два способа объявления некоторой $scope переменной как наблюдаемой (читайте ниже).

$ watch помогает отслеживать изменения $ scope

Есть два способа объявить переменную $scope как наблюдаемую.

  1. Используя его в своем шаблоне через выражение <span>{{myVar}}</span>
  2. Добавляя его вручную через службу $watch

Объявление 1) Это наиболее распространенный сценарий, и я уверен, что вы видели его раньше, но не знали, что это создает часы в фоновом режиме. Да, было! Использование директив AngularJS (например, ng-repeat) также может создавать неявные часы.

Объявление 2) Таким образом вы создаете свои собственные часы. Служба $watch помогает запустить код, когда какое-то значение, прикрепленное к $scope, изменилось. Используется редко, но иногда бывает полезным. Например, если вы хотите запускать какой-то код каждый раз при изменении myVar, вы можете сделать следующее:

function MyController($scope) {

    $scope.myVar = 1;

    $scope.$watch('myVar', function() {
        alert('hey, myVar has changed!');
    });

    $scope.buttonClicked = function() {
        $scope.myVar = 2; // This will trigger $watch expression to kick in
    };
}

$ apply позволяет интегрировать изменения в цикл дайджеста

Вы можете думать о $apply функции как о механизме интеграции. Видите ли, каждый раз, когда вы изменяете некоторую наблюдаемую переменную, прикрепленную к объекту $scope напрямую, AngularJS будет знать, что изменение произошло. Это потому, что AngularJS уже знал, как отслеживать эти изменения. Так что, если это происходит в коде, управляемом фреймворком, цикл дайджеста будет продолжен.

Однако иногда вы хотите изменить какое-либо значение за пределами мира AngularJS и увидеть, как изменения распространяются нормально. Учтите это - у вас есть значение $scope.myVar, которое будет изменено в обработчике $.ajax() jQuery. Это произойдет когда-нибудь в будущем. AngularJS не может дождаться, когда это произойдет, поскольку ему не было предписано ждать на jQuery.

Для решения этой проблемы был введен $apply. Это позволяет вам явно запустить цикл пищеварения. Однако вы должны использовать это только для переноса некоторых данных в AngularJS (интеграция с другими фреймворками), но никогда не используйте этот метод в сочетании с обычным кодом AngularJS, поскольку тогда AngularJS выдаст ошибку.

Как все это связано с DOM?

Что ж, вам действительно стоит снова следовать руководству, теперь, когда вы все это знаете. Цикл дайджеста гарантирует, что пользовательский интерфейс и код JavaScript остаются синхронизированными, путем оценки каждого наблюдателя, прикрепленного ко всем $scopes, пока ничего не изменится. Если в цикле дайджеста больше не происходит изменений, он считается завершенным.

Вы можете присоединять объекты к объекту $scope либо явно в Контроллере, либо путем объявления их в форме {{expression}} непосредственно в представлении.

Я надеюсь, что это поможет прояснить некоторые базовые знания обо всем этом.

Дальнейшие чтения:

person ŁukaszBachman    schedule 27.02.2013
comment
как насчет использования этого? (Контроль как метод) - person Leandro Bardelli; 08.09.2014
comment
Использование Control as не должно влиять на приведенную выше информацию. Использование this.myVar помещает myVar в область видимости. - person Marcus Rådell; 03.10.2014
comment
@ ŁukaszBachman - тогда вы явно указываете Angular отслеживать изменения. Я думаю, что это «неявно», а не «явно» - person mia; 09.01.2015

В AngularJS мы обновляем наши модели, а наши представления / шаблоны обновляют DOM «автоматически» (через встроенные или настраиваемые директивы).

$ apply и $ watch, оба являются методами Scope, не связаны с DOM.

На странице Concepts (раздел «Время выполнения») есть довольно хорошее объяснение цикла $ digest, $ apply, $ evalAsync очередь и список наблюдения $. Вот картинка, которая сопровождает текст:

$ digest loop

Любой код, имеющий доступ к области видимости, обычно контроллеры и директивы (их функции связывания и / или их контроллеры) могут настроить "watchExpression", который AngularJS будет оценивать в соответствии с этой областью. Эта оценка происходит всякий раз, когда AngularJS входит в свой цикл $ digest (в частности, цикл "$ watch list"). Вы можете просматривать отдельные свойства области видимости, вы можете определить функцию для просмотра двух свойств вместе, вы можете отслеживать длину массива и т. Д.

Когда что-то происходит «внутри AngularJS», например, вы вводите текст в текстовое поле, в котором включена двусторонняя привязка данных AngularJS (т.е. использует ng-модель), срабатывает обратный вызов $ http и т. Д. $ Apply уже был вызван, поэтому мы внутри прямоугольника AngularJS на рисунке выше. Все выражения watchExpressions будут оцениваться (возможно, более одного раза, пока не будут обнаружены дальнейшие изменения).

Когда что-то происходит «вне AngularJS», например, вы использовали bind () в директиве, а затем это событие срабатывает, в результате чего вызывается ваш обратный вызов, или срабатывает какой-то зарегистрированный обратный вызов jQuery, мы все еще находимся в «собственном» прямоугольнике. Если код обратного вызова изменяет все, что наблюдает $ watch, вызовите $ apply, чтобы попасть в прямоугольник AngularJS, в результате чего запустится цикл $ digest, и, следовательно, AngularJS заметит изменение и сотворит чудеса.

person Mark Rajcok    schedule 28.02.2013
comment
Я понимаю идею, но не понимаю, как на самом деле передаются данные. У меня есть модель, которая представляет собой объект с большим количеством данных, я использую некоторые из них для управления DOM. потом кое-что меняется. Как мне поместить измененные данные в нужное место в модели? В примере, который я использовал, он выполняет манипуляции и в конце просто использует scope.$apply(scope.model), я не понимаю, какие данные передаются и как они передаются в нужное место в модели? - person ilyo; 28.02.2013
comment
Никакой волшебной передачи данных не происходит. Обычно с приложениями Angular вы должны изменить модели Angular, которые затем управляют обновлениями просмотра / DOM. Если вы обновляете DOM вне Angular, вам придется вручную обновлять модели. scope.$apply(scope.model) просто оценит scope.model как выражение Angular, а затем войдет в цикл $ digest. В статье, на которую вы ссылаетесь, вероятно, будет достаточно scope.$apply(), поскольку модель уже отслеживается. Функция stop () обновляет модель (я считаю, что toUpdate - это ссылка на scope.model), а затем вызывается $ apply. - person Mark Rajcok; 28.02.2013
comment
Похоже, что документы AngularJS переместились из-под этого ответа (первая ссылка не имеет времени выполнения или $watch на странице, а вторая ссылка не работает - на данный момент, во всяком случае). Ужасно, архивные версии не кэшировал какой-либо асинхронный процесс, создавший контент. - person ruffin; 15.01.2020

AngularJS расширяет этот цикл событий, создавая нечто под названием AngularJS context.

$ watch ()

Каждый раз, когда вы что-то привязываете в пользовательском интерфейсе, вы вставляете $watch в $watch список.

User: <input type="text" ng-model="user" />
Password: <input type="password" ng-model="pass" />

Здесь у нас есть $scope.user, который привязан к первому входу, и у нас есть $scope.pass, который привязан ко второму. При этом мы добавляем два $watches в список $watch.

Когда наш шаблон загружен, он же на этапе компоновки, компилятор будет искать каждую директиву и создает все необходимые $watches.

AngularJS предоставляет $watch, $watchcollection и $watch(true). Ниже приведена аккуратная диаграмма, объясняющая все три, взятые из подробные сведения о наблюдателях.

Введите описание изображения здесь

angular.module('MY_APP', []).controller('MyCtrl', MyCtrl)
function MyCtrl($scope,$timeout) {
  $scope.users = [{"name": "vinoth"},{"name":"yusuf"},{"name":"rajini"}];

  $scope.$watch("users", function() {
    console.log("**** reference checkers $watch ****")
  });

  $scope.$watchCollection("users", function() {
    console.log("**** Collection  checkers $watchCollection ****")
  });

  $scope.$watch("users", function() {
    console.log("**** equality checkers with $watch(true) ****")
  }, true);

  $timeout(function(){
     console.log("Triggers All ")
     $scope.users = [];
     $scope.$digest();

     console.log("Triggers $watchCollection and $watch(true)")
     $scope.users.push({ name: 'Thalaivar'});
     $scope.$digest();

     console.log("Triggers $watch(true)")
     $scope.users[0].name = 'Superstar';
     $scope.$digest();
  });
}

http://jsfiddle.net/2Lyn0Lkb/

$digest петля

Когда браузер получает событие, которым можно управлять с помощью контекста AngularJS, запускается цикл $digest. Эта петля состоит из двух петель меньшего размера. Один обрабатывает очередь $evalAsync, а другой обрабатывает $watch list. $digest будет перебирать список $watch, который у нас есть

app.controller('MainCtrl', function() {
  $scope.name = "vinoth";

  $scope.changeFoo = function() {
      $scope.name = "Thalaivar";
  }
});

{{ name }}
<button ng-click="changeFoo()">Change the name</button>

Здесь у нас только один $watch, потому что ng-click не создает никаких часов.

Нажимаем кнопку.

  1. Браузер получает событие, которое войдет в контекст AngularJS.
  2. Цикл $digest запустится и будет запрашивать изменения каждые $ watch.
  3. Поскольку $watch, который следил за изменениями в $ scope.name, сообщает об изменении, он вызовет еще один цикл $digest.
  4. Новый цикл ничего не сообщает.
  5. Браузер вернет элемент управления и обновит DOM, отражая новое значение $ scope.name.
  6. Здесь важно то, что КАЖДОЕ событие, которое входит в контекст AngularJS, запускает цикл $digest. Это означает, что каждый раз, когда мы вводим букву, цикл будет проверять каждый $watch на этой странице.

$ применить ()

Если вы вызовете $apply при запуске события, оно пройдет через контекст angular, но если вы его не вызовете, оно будет выполняться вне его. Это так просто. $apply вызовет цикл $digest() внутри, и он будет перебирать все часы, чтобы гарантировать, что DOM обновляется новым обновленным значением.

Метод $apply() будет запускать наблюдателей для всей цепочки $scope, тогда как метод $digest() будет запускать наблюдателей только для текущего $scope и его children. Если ни один из вышестоящих $scope объектов не должен знать о локальных изменениях, можно использовать $digest().

person Thalaivar    schedule 22.04.2016

Я нашел очень подробные видео, которые охватывают $watch, $apply, $digest и дайджест циклы в:

Ниже приведены несколько слайдов, которые используются в этих видеороликах для объяснения концепций (на всякий случай, если указанные выше ссылки удалены / не работают).

Введите здесь описание изображения

На изображении выше "$ scope.c" не отслеживается, поскольку он не используется ни в одной из привязок данных (в разметке). За двумя другими ($scope.a и $scope.b) будут наблюдать.

Введите здесь описание изображения

Из приведенного выше изображения: на основе соответствующего события браузера AngularJS фиксирует событие, выполняет цикл дайджеста (просматривает все наблюдения на предмет изменений), выполняет функции наблюдения и обновляет DOM. Если это не события браузера, цикл дайджеста можно запустить вручную с помощью $apply или $digest.

Подробнее о $apply и $digest:

Введите здесь описание изображения

person user203687    schedule 20.11.2016

Также есть $watchGroup и $watchCollection. В частности, $watchGroup действительно полезен, если вы хотите вызвать функцию для обновления объекта, который имеет несколько свойств в представлении, которое не является объектом dom, например другой вид на холсте, WebGL или запрос сервера.

Здесь ссылка.

person Utkarsh Bhardwaj    schedule 18.03.2015
comment
Я бы прокомментировал $watchCollection, но я вижу, что вы уже это сделали. Вот документация об этом с сайта AngularJS. Они обеспечивают очень красивое изображение $watch глубины. Обратите внимание, что информация находится внизу страницы. - person JabberwockyDecompiler; 01.04.2015

Просто дочитайте ВСЕ вышеперечисленное, скучно и сонно (извините, но это правда). Очень технический, всесторонний, подробный и сухой. Почему я пишу? Поскольку AngularJS огромен, множество взаимосвязанных концепций могут свести с ума любого. Я часто спрашивал себя, достаточно ли я умен, чтобы их понять? Нет! Это потому, что очень немногие могут объяснить технологию на языке для начинающих без всякой терминологии! Хорошо, позволь мне попробовать:

1) Все они связаны с событиями. (Я слышу смех, но читаю дальше)

Если вы не знаете, что такое событие, управляемое событиями. Тогда подумайте, что вы размещаете кнопку на странице, подключаете ее к функции, используя функцию on-click, ожидая, пока пользователи нажмут на нее, чтобы инициировать действия, которые вы вставляете внутри функции. Или подумайте о триггере SQL Server / Oracle.

2) $ watch запускается по клику.

Что особенного в том, что он принимает две функции в качестве параметров, первая дает значение из события, вторая принимает значение во внимание ...

3) $ digest - это начальник, который без устали проверяет все вокруг, бла-бла-бла, но хороший хозяин.

4) $ apply дает вам возможность, когда вы хотите сделать это вручную, как отказоустойчивый (если при нажатии не срабатывает, вы заставляете его запускаться).

Теперь давайте сделаем это наглядным. Представьте себе это, чтобы вам было еще проще понять идею:

В ресторане

- ОФИЦИАНТЫ

должны принимать заказы от клиентов, это

$watch(
  function(){return orders;},
  function(){Kitchen make it;}
);

- МЕНЕДЖЕР бегает, чтобы убедиться, что все официанты бодрствуют и реагируют на любые признаки изменений от клиентов. Это $digest()

- ВЛАДЕЛЕЦ может управлять всеми по запросу, это $apply()

person Jeb50    schedule 07.02.2017
comment
Это может понять пятилетний ребенок. Я ценю такой ответ. +1 - person Chris22; 05.10.2019
comment
Я часто спрашивал себя, неужели я недостаточно умен, чтобы их понять? Нет! извиняюсь. Я смеялся. - person ruffin; 12.11.2020