Вторник, 30 мая, 2017

AngularJS под капотом: $apply() и $digest()

комментарии: 4

Когда нужно самому вызывать $apply()?

Итак, после выполнения нашего кода AngularJS сам вызывает функцию $apply() и запускает цикл $digest(). Тогда, в каких ситуациях разработчику необходимо явно вызвать $apply()? В действительности AngularJS делает одну простую вещь. Он отслеживает только те изменения модели, которые сделаны в контексте среды AngularJS, то есть в тех функциях, которые неявно выполняются внутри $apply(). Так реализованы встроенные директивы AngularJS, - любое изменение модели отражается в представлении. Но если вы измените модель вне контекcта AngularJS, то необходимо сообщить среде AngularJS об изменениях посредством явного вызова функции $apply(). Тем самым вы говорите, что изменились некоторые модели и должны быть вызваны все watcher-функции для передачи изменений.

Например, если вы используете не сервис $timeout, а Javascript-функцию setTimeout, чтобы изменить модель, то AngularJS никак не может узнать об этих изменениях. Это тот случай, когда разработчик сам должен прописать вызов функции $apply(), которая инициирует запуск цикла $digest(). Аналогично, если вы пишете директиву, внутри которой реализована функция-событие какого-нибудь DOM-элемента и внутри этой функции изменяются модели, то также нужно вызвать явно $apply(), чтобы изменения произвели нужный эффект.

Рассмотрим пример. Допустим на странице после загрузки через две секунды должно появится сообщение. Реализовали на Javascript так:

Запускаем пример, через две секунды в функции таймера изменяется модель message. Но на представлении это никак не отразилось. Причина в том, что не вызвана явно функция $apply(). Исправим код, как показано ниже:

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

Замечание: Это гипотетический пример, для показа разницы, на самом деле лучше использовать вместо setTimeout, встроенный в AngularJS сервис $timeout, который автоматически неявно вызовет $apply() и об этом не надо заботиться разработчику.

Также обратим внимание, что можно сделать изменения модели и затем вызвать в конце функцию $apply() без аргументов. Так, как это cделано ниже:

$scope.getMessage = function() {
    setTimeout(function() {
        $scope.message = 'Прошло две секунды.'; 
        console.log('message:' + $scope.message); 
        $scope.$apply(); // эта функция запустит $digest 
    }, 2000); 
};

В этом примере используется безаргументная функция и она работает. Но лучше передавать свою функцию в качестве аргумента в $apply(), так как она выполнится внутри блока try … catch, и все исключения будут передаваться в сервис $exceptionHandler.

Комментарии

Роман Роман 12 июня, 2015, 07:48
Супер . Очень хорошо изложено .
Дмитрий Дмитрий 31 июля, 2015, 03:09
Зачет. Все изложено нормальным, доступным, человеческим языком. Сайт в закладки !
Yaroslav Draga Yaroslav Draga 20 сентября, 2016, 08:42
обычно по ангуляру статьи муторные, сложные для понимания, но эта заходит на ура, пожалуйста, добавьте по ангуляру больше статтей
юля юля 28 февраля, 2017, 11:55
супер

 

Добавить комментарий

Войти через Twitter Войти через Facebook Войти через ВКонтакте Войти через Github Войти через Google Войти через LinkedIn

Copyright © 2013 All Rights Reserved for CodeHint.ru      Powered by Windows Azure