Как использовать $scope.$ Watch и $scope.$ Apply в AngularJS?
Я не понимаю как пользоваться $scope.$watch
а также $scope.$apply
, Официальная документация не помогает.
Что я не понимаю конкретно:
- Они связаны с DOM?
- Как я могу обновить изменения DOM в модели?
- Какая связь между ними?
Я пробовал этот урок, но он требует понимания $watch
а также $apply
как должное.
Что $apply
а также $watch
и как их правильно использовать?
7 ответов
Вы должны знать о том, как работает AngularJS, чтобы понять это.
Цикл дайджеста и объем $
Прежде всего, AngularJS определяет концепцию так называемого цикла дайджеста. Этот цикл можно рассматривать как цикл, во время которого AngularJS проверяет, есть ли какие-либо изменения во всех переменных, наблюдаемых всеми $scope
s. Так что если у вас есть $scope.myVar
определены в вашем контроллере, и эта переменная была помечена для наблюдения, а затем вы неявно говорите AngularJS отслеживать изменения на myVar
в каждой итерации цикла.
Естественным последующим вопросом будет: все ли прикреплено к $scope
смотреть? К счастью, нет. Если бы вы следили за изменениями каждого объекта в вашем $scope
затем, чтобы быстро оценить цикл дайджеста, потребуются проблемы с производительностью. Вот почему команда AngularJS дала нам два способа объявить некоторые $scope
переменная как наблюдаемый (читайте ниже).
$ watch помогает прослушивать изменения $scope
Есть два способа объявить $scope
переменная как наблюдаемый.
- Используя его в своем шаблоне через выражение
<span>{{myVar}}</span>
- Добавляя его вручную через
$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
значение, которое будет изменено в JQuery's $.ajax()
обработчик. Это произойдет в какой-то момент в будущем. AngularJS не может ждать, пока это произойдет, так как ему не было приказано ждать на jQuery.
Чтобы справиться с этим, $apply
был введен. Это позволяет вам начать цикл пищеварения явно. Однако вы должны использовать это только для переноса некоторых данных в AngularJS (интеграция с другими фреймворками), но никогда не используйте этот метод в сочетании с обычным кодом AngularJS, так как тогда AngularJS выдаст ошибку.
Как все это связано с DOM?
Что ж, вы должны действительно следовать учебнику снова, теперь, когда вы все это знаете. Цикл дайджеста обеспечит синхронизацию пользовательского интерфейса и кода JavaScript путем оценки каждого наблюдателя, подключенного ко всем $scope
пока ничего не меняется. Если в цикле дайджеста больше не происходит изменений, то он считается завершенным.
Вы можете прикрепить объекты к $scope
Объект явно либо в контроллере, либо объявив их в {{expression}}
Форма непосредственно в представлении.
Я надеюсь, что это поможет уточнить некоторые базовые знания обо всем этом.
Дальнейшие чтения:
В AngularJS мы обновляем наши модели, а наши представления / шаблоны обновляют DOM "автоматически" (через встроенные или пользовательские директивы).
$apply и $watch, оба являются методами Scope, не связаны с DOM.
Страница Concepts (раздел "Время выполнения") содержит довольно хорошее объяснение цикла $digest, $apply, очереди $evalAsync и списка $ watch. Вот картинка, которая сопровождает текст:
Какой бы код не имел доступ к области видимости - обычно это контроллеры и директивы (их функции связи и / или их контроллеры) - может установить " watchExpression", которое AngularJS будет оценивать по отношению к этой области. Эта оценка происходит всякий раз, когда AngularJS входит в свой цикл $digest (в частности, цикл "$watch list"). Вы можете просматривать отдельные свойства области, вы можете определить функцию для просмотра двух свойств вместе, вы можете наблюдать длину массива и т. Д.
Когда что-то происходит "внутри AngularJS" - например, вы вводите текстовое поле, в котором включена двусторонняя привязка данных AngularJS (т. Е. Используется ng-модель), срабатывает обратный вызов $http и т. Д. - $apply уже был вызван, поэтому мы Вы находитесь внутри прямоугольника "AngularJS" на рисунке выше. Все watchExpressions будут оцениваться (возможно, более одного раза - до тех пор, пока дальнейшие изменения не будут обнаружены).
Когда что-то происходит "вне AngularJS" - например, вы использовали bind() в директиве, а затем это событие вызывается, в результате чего вызывается ваш обратный вызов или запускаются некоторые зарегистрированные вызовы обратного вызова jQuery - мы все еще находимся в прямоугольнике "Native". Если код обратного вызова изменяет что-либо, что наблюдает любой $watch, вызовите $apply, чтобы попасть в прямоугольник AngularJS, в результате чего запустится цикл $digest, и, следовательно, AngularJS заметит это изменение и сделает его магию.
В этом блоге были освещены все это, создавая примеры и понятные объяснения.
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 возвращает $scope
переменная scope.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()
перебирает часы, вызывает функцию значения для каждого из часов. Если значение, возвращаемое функцией-значением, отличается от значения, возвращенного при последнем вызове, вызывается функция прослушивателя для этих часов.
$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()
внутренне, так что все привязки данных обновляются.
AngularJS расширяет этот цикл событий, создавая то, что называется AngularJS context
,
$ часы ()
Каждый раз, когда вы связываете что-то в пользовательском интерфейсе, вы вставляете $watch
в $watch
список
User: <input type="text" ng-model="user" />
Password: <input type="password" ng-model="pass" />
Здесь мы имеем $scope.user
, который привязан к первому входу, и мы имеем $scope.pass
, который связан со вторым. Делая это, мы добавляем два $watch
это к $watch
список
Когда наш шаблон загружен, AKA на этапе компоновки, компилятор ищет каждую директиву и создает все $watch
Если это необходимо.
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();
});
}
$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
потому что нг-клик не создает никаких часов.
Нажимаем кнопку.
- Браузер получает событие, которое войдет в контекст AngularJS
-
$digest
цикл запустится и будет запрашивать у каждого $watch изменения. - Так как
$watch
который следил за изменениями в $scope.name сообщает об изменении, это заставит другого$digest
петля. - Новый цикл ничего не сообщает.
- Браузер возвращает элемент управления и обновляет DOM, отражая новое значение $scope.name
- Здесь важно то, что КАЖДОЕ событие, которое входит в контекст AngularJS, будет запускать
$digest
петля. Это означает, что каждый раз, когда мы пишем письмо на входе, цикл будет запускать проверку каждого$watch
на этой странице.
$ Применяются ()
Если вы позвоните $apply
когда событие запускается, оно проходит через угловой контекст, но если вы его не вызываете, оно выходит за его пределы. Это так просто. $apply
позвоню $digest()
внутренний цикл, и он будет перебирать все часы, чтобы гарантировать, что DOM будет обновлен с новым обновленным значением.
$apply()
Метод вызовет наблюдателей на весь $scope
цепь, тогда как $digest()
метод будет вызывать только наблюдатели на текущий $scope
И его children
, Когда никто из высших $scope
объекты должны знать о локальных изменениях, вы можете использовать $digest()
,
Я нашел очень подробные видео, которые охватывают $watch
, $apply
, $digest
и переварить циклы в:
AngularJS - Понимание Watcher, $ watch, $ watchGroup, $ watchCollection, ng-change
AngularJS - Понимание цикла дайджеста (фаза дайджеста, процесс дайджеста или цикл дайджеста)
Ниже приведено несколько слайдов, используемых в этих видеороликах для объяснения концепции (на всякий случай, если вышеуказанные ссылки удалены / не работают).
На изображении выше "$scope.c" не отслеживается, поскольку не используется ни в одной из привязок данных (в разметке). Другие два ($scope.a
а также $scope.b
) будет смотреться.
На изображении выше: на основе соответствующего события браузера AngularJS захватывает событие, выполняет цикл дайджеста (проходит все отслеживания изменений), выполняет функции отслеживания и обновляет DOM. Если не события браузера, цикл дайджеста может быть запущен вручную с помощью $apply
или же $digest
,
Больше о $apply
а также $digest
:
Есть $watchGroup
а также $watchCollection
также. В частности, $watchGroup
действительно полезно, если вы хотите вызвать функцию для обновления объекта, который имеет несколько свойств в представлении, которое не является объектом dom, например, для другого представления в canvas, webGL или запросе к серверу. Здесь ссылка на документацию.
Просто закончите читать ВСЕ выше, скучно и сонно (извините, но это правда). Очень технический, глубокий, подробный и сухой. Почему я пишу? Поскольку AngularJS огромен, множество взаимосвязанных концепций может свести с ума любого. Я часто спрашивал себя, я не достаточно умен, чтобы понять их? Нет! Это потому, что очень немногие могут объяснить технологию на пустышке без всякой терминологии! Хорошо, позвольте мне попробовать:
1) Все это вещи, управляемые событиями. (Я слышу смех, но продолжаю читать)
Если вы не знаете, что такое событие, управляемое событиями, то подумайте, что вы помещаете кнопку на страницу, подключаете ее с помощью функции "по нажатию", ожидая, пока пользователи нажмут на нее, чтобы запустить действия, которые вы внедрите внутри функция. Или подумайте о "триггере" SQL Server / Oracle.
2) $ watch "на клике".
Что особенного в том, что он принимает 2 функции в качестве параметров, первая дает значение из события, вторая учитывает значение...
3) $ digest - это босс, который неустанно проверяет, бла-бла-бла, но хороший босс.
4) $ apply дает вам способ, когда вы хотите сделать это вручную, например, отказоустойчивость (в случае, если щелчок не срабатывает, вы заставляете его запускаться).
Теперь давайте сделаем это визуально. Изобразите это, чтобы сделать идею еще проще:
В ресторане,
- Официанты должны принимать заказы от клиентов, это
$watch(
function(){return orders;},
function(){Kitchen make it;}
);
- МЕНЕДЖЕР бегает, чтобы убедиться, что все официанты бодрствуют, реагируя на любые признаки изменений со стороны клиентов. Это $digest()
- ВЛАДЕЛЕЦ имеет максимальную силу, чтобы водить всех по запросу, это $apply()