it-swarm.com.ru

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

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

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

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

Я попытался это руководство , но понимание $watch и $apply само собой разумеющимся.

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

1058
ilyo

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

Цикл дайджеста и объем $

Прежде всего, 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, оценивая каждый наблюдатель, прикрепленный ко всем $scope, до тех пор, пока ничего не изменится. Если в цикле дайджеста больше не происходит изменений, то он считается завершенным.

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

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

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

1709
ŁukaszBachman

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

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

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

$digest loop

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

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

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

157
Mark Rajcok

этот блог было рассмотрено все, что создает примеры и понятные объяснения.

Функции AngularJS $scope$watch(), $digest() и $apply() являются одними из центральных функций AngularJS. Понимание $watch(), $digest() и $apply() необходимо для понимания AngularJS.

Когда вы создаете привязку данных откуда-то в вашем представлении к переменной объекта $ scope, AngularJS создает "наблюдение" внутри. Наблюдение означает, что AngularJS отслеживает изменения в переменной $scope object. Фреймворк "наблюдает" за переменной. Часы создаются с помощью функции $scope.$watch(), о которой я расскажу позже в этом тексте.

В ключевых точках вашего приложения AngularJS вызывает функцию $scope.$digest(). Эта функция просматривает все наблюдения и проверяет, изменилась ли какая-либо из наблюдаемых переменных. Если наблюдаемая переменная изменилась, вызывается соответствующая функция слушателя. Функция слушателя выполняет любую работу, например, изменяя текст HTML, чтобы отразить новое значение наблюдаемой переменной. Таким образом, функция $digest() запускает привязку данных для обновления.

В большинстве случаев AngularJS будет вызывать для вас функции $ scope. $ Watch () и $scope.$digest(), но в некоторых ситуациях вам, возможно, придется вызывать их самостоятельно. Поэтому очень хорошо знать, как они работают.

Функция $scope.$apply() используется для выполнения некоторого кода, а затем после этого вызывается $scope.$digest(), поэтому проверяются все часы и вызываются соответствующие функции прослушивателя часов. Функция $apply() полезна при интеграции AngularJS с другим кодом.

Я подробнее расскажу о функциях $watch(), $digest() и $apply() в оставшейся части этого текста.

$ Часы ()

Функция $scope.watch() создает наблюдение за некоторой переменной. При регистрации часов вы передаете две функции в качестве параметров функции $watch():

  • Функция значения
  • Функция слушателя

Вот пример:

$scope.$watch(function() {},
              function() {}
             );

Первая функция - это функция значения, а вторая функция - функция слушателя.

Функция value должна возвращать значение, которое отслеживается. Затем AngularJS может проверить возвращаемое значение по сравнению со значением, которое функция watch вернула в прошлый раз. Таким образом AngularJS может определить, изменилось ли значение. Вот пример:

$scope.$watch(function(scope) { return scope.data.myVar },
              function() {}
             );

В этом примере функция valule возвращает переменную $scopescope.data.myVar. Если значение этой переменной изменится, будет возвращено другое значение, и AngularJS вызовет функцию слушателя.

Обратите внимание, как функция значения принимает область видимости в качестве параметра (без $ в имени). Через этот параметр функция значения может получить доступ к $scope и его переменным. Функция value также может отслеживать глобальные переменные, если вам это нужно, но чаще всего вы будете наблюдать переменную $scope.

Функция слушателя должна делать все, что ей нужно, если значение изменилось. Возможно, вам нужно изменить содержимое другой переменной или установить содержимое элемента HTML или чего-то еще. Вот пример:

$scope.$watch(function(scope) { return scope.data.myVar },
              function(newValue, oldValue) {
                  document.getElementById("").innerHTML =
                      "" + newValue + "";
              }
             );

В этом примере для внутреннего HTML-элемента HTML устанавливается новое значение переменной, встроенное в элемент b, в результате чего значение выделяется жирным шрифтом. Конечно, вы могли бы сделать это, используя код {{ data.myVar }, но это всего лишь пример того, что вы можете сделать внутри функции слушателя.

$ Дайджеста ()

Функция $scope.$digest() выполняет итерацию по всем элементам наблюдения в $scope object и ее дочерним объектам $ scope (если они есть). Когда $digest() перебирает часы, он вызывает функцию value для каждого из часов. Если значение, возвращаемое функцией-значением, отличается от значения, возвращенного при последнем вызове, вызывается функция прослушивателя для этих часов.

Функция $digest() вызывается всякий раз, когда AngularJS считает это необходимым. Например, после выполнения обработчика нажатия кнопки или после возврата вызова AJAX (после выполнения функции обратного вызова done ()/fail ()).

Вы можете столкнуться с некоторыми угловыми случаями, когда AngularJS не вызывает функцию $digest() для вас. Обычно вы обнаруживаете это, замечая, что привязки данных не обновляют отображаемые значения. В этом случае вызовите $scope.$digest(), и это должно сработать. Или, возможно, вы можете использовать вместо этого $scope.$apply(), что я объясню в следующем разделе.

$ Применяются ()

Функция $scope.$apply() принимает функцию в качестве параметра, который выполняется, и после этого $scope.$digest() вызывается изнутри. Это облегчает вам проверку всех часов и, таким образом, обновляет все привязки данных. Вот пример $apply():

$scope.$apply(function() {
    $scope.data.myVar = "Another value";
});

Функция, переданная функции $apply() в качестве параметра, изменит значение $scope.data.myVar. При выходе из функции AngularJS вызовет функцию $scope.$digest(), чтобы все часы проверялись на предмет изменений наблюдаемых значений.

Пример

Чтобы показать, как работают $watch(), $digest() и $apply(), посмотрите на этот пример:

<div ng-controller="myController">
    {{data.time}}

    <br/>
    <button ng-click="updateTime()">update time - ng-click</button>
    <button id="updateTimeButton"  >update time</button>
</div>


<script>
    var module       = angular.module("myapp", []);
    var myController1 = module.controller("myController", function($scope) {

        $scope.data = { time : new Date() };

        $scope.updateTime = function() {
            $scope.data.time = new Date();
        }

        document.getElementById("updateTimeButton")
                .addEventListener('click', function() {
            console.log("update time clicked");
            $scope.data.time = new Date();
        });
    });
</script>

его пример связывает переменную $scope.data.time с директивой интерполяции, которая объединяет значение переменной с HTML-страницей. Эта привязка создает часы внутри $scope.data.time variable.

Пример также содержит две кнопки. К первой кнопке прикреплен слушатель ng-click. При нажатии этой кнопки вызывается функция $scope.updateTime(), а после этого AngularJS вызывает функцию $scope.$digest(), чтобы привязки данных обновлялись.

Вторая кнопка получает стандартный приемник событий JavaScript, присоединенный к ней изнутри функции контроллера. При нажатии второй кнопки эта функция слушателя выполняется. Как видите, функции прослушивателя для обеих кнопок работают почти одинаково, но когда вызывается функция прослушивателя второй кнопки, привязка данных не обновляется. Это связано с тем, что функция $scope.$digest() не вызывается после выполнения прослушивателя событий второй кнопки. Таким образом, при нажатии второй кнопки время обновляется в переменной $scope.data.time, но новое время никогда не отображается.

Чтобы это исправить, мы можем добавить вызов $scope.$digest() в последнюю строку прослушивателя событий кнопки, например так:

document.getElementById("updateTimeButton")
        .addEventListener('click', function() {
    console.log("update time clicked");
    $scope.data.time = new Date();
    $scope.$digest();
});

Вместо вызова $digest() внутри функции прослушивателя кнопок, вы могли бы также использовать функцию $apply(), например:

document.getElementById("updateTimeButton")
        .addEventListener('click', function() {
    $scope.$apply(function() {
        console.log("update time clicked");
        $scope.data.time = new Date();
    });
});

Обратите внимание, как функция $scope.$apply() вызывается из прослушивателя событий кнопки, и как выполняется обновление переменной $scope.data.time внутри функции, передаваемой в качестве параметра функции $apply(). Когда вызов функции $apply() завершается, AngularJS вызывает $digest() изнутри, поэтому все привязки данных обновляются.

61
Alex Jolig

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

$ часы ()

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

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

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

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

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

Enter image description here

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 при возникновении события, оно проходит через угловой контекст, но если вы его не вызываете, оно будет запущено за его пределы. Это так просто. $apply ВЫЗОВЕТ ВНУТРЕННИЙ ЦИКЛ $digest() и выполнит итерацию по всем контрольным точкам, чтобы убедиться, что DOM обновляется с новым обновленным значением.

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

45
Thalaivar

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

17
Utkarsh Bhardwaj

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

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

Enter image description here

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

Enter image description here

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

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

Enter image description here

17
user203687

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

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

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

2) $ watch "по нажатию".

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

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

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

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

В ресторане

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

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

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

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

12
Jeb50