Когда безопасно использовать $scope. $Apply()?
Я думаю, что название довольно ясно, что я прошу. Я создал эту скрипту: http://jsfiddle.net/Sourabh_/HB7LU/13142/
В скрипке я попытался воспроизвести сценарий async
. Это всего лишь пример, но в вызове AJAX, если я не использую $scope.$apply()
, список не обновляется. Я хочу знать, можно ли использовать $scope.$apply()
каждый раз, когда я делаю вызов AJAX для обновления списка или есть какой-то другой механизм, который я могу использовать?
Код, который я написал для репликации сценария (так же, как в скрипке):
HTML
<div ng-controller="MyCtrl">
<li ng-repeat="item in items">
{{item.name}}
</li>
<button ng-click="change()">Change</button>
</div>
JS
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.items = [{name : "abc"},{name : "xyz"},{name : "cde"}];
$scope.change = function(){
test(function(testItem){
$scope.items = testItem;
//$scope.$apply();
})
}
function test(callback){
var testItem = [
{name : "mno"},
{name : "pqr"},
{name : "ste"}
];
setTimeout(function(){callback(testItem)},2000);
}
}
Ответы
Ответ 1
Если вы хотите использовать API-Rest-Call, используйте возвращенное promise
в вашем Controller
вместо этого устанавливая область действия внутри Rest-Call.
$http.get('uri')
.success(function(data) {
$scope.items = data
});
Избегайте использования $apply()
. Из углового репо GitHub:
$scope.$apply()
должно появляться как можно ближе к привязке асинхронного события.
НЕ случайным образом разбрызгивайте его по всему коду. Если вы делаете, если (!$scope.$$phase) $scope.$apply()
это потому, что вы недостаточно высоки в стеке вызовов.
На ваш вопрос:
- Если вы оказались в ситуации, когда вам нужен $ apply(), переосмыслите свою структуру.
- Просто из соображений безопасности: никогда не используйте
$apply()
Ответ 2
Изменить Не было ясно, что OP пытался издеваться над обратным вызовом. Тем не менее, использование службы $timeout
- отличный способ избежать необходимости вызова $scope.$apply
вручную и является более общеприемлемым решением, чем использование Promise (в тех случаях, когда вы не вызываете $http
), всегда иметь смысл заставить свои изменения перейти в следующий цикл, обернув их обещанием).
Обновите свой код, чтобы использовать $timeout service, и он должен работать без вызова $apply
.
$timeout
- это оболочка вокруг нативного setTimeout
с важным отличием: $timeout
задерживает выполнение, по крайней мере, до следующего цикла $digest
.
Таким образом, передача без задержки будет задерживать выполнение до следующего цикла. Передача в 2000 году приведет к задержке выполнения до следующего цикла после 2000 мс.
Следовательно, это простой трюк, чтобы убедиться, что ваши изменения подхвачены Angular без необходимости называть $apply
вручную (что в любом случае считается небезопасным)
function MyCtrl($scope, $timeout) {
$scope.items = [{name : "abc"},{name : "xyz"},{name : "cde"}];
$scope.change = function(){
test(function(testItem){
$scope.items = testItem;
//$scope.$apply();
})
}
function test(callback){
var testItem = [
{name : "mno"},
{name : "pqr"},
{name : "ste"}
];
$timeout(function(){callback(testItem)},2000);
}
}
Ответ 3
$apply, следует использовать, когда код не выполняется в цикле angular digest. В обычных условиях нам не нужно будет использовать его, но мы можем использовать его, если у нас есть код, который вызывается из обработчика события jQuery или из таких методов, как setTimeout()
. Даже если у вас есть функция, вызываемая из другой функции angular, такой как обработчики событий watch
или angular, вам не нужно использовать $apply(), поскольку эти сценарии выполняются в цикле дайджест.
Один безопасный способ - проверить параметр $scope.$$phase
перед вызовом $scope. $apply(), например
if($scope.$$phase){
$scope.$apply();
}
В вашем случае, но вы можете использовать $timeout, как предложено в другом ответе
Ответ 4
Вам нужно использовать $apply каждый раз, когда вы используете что-то, что не является "angular способом", например, Anzeo рассказал о $timeout.
Например, если вы используете jQuery http вместо angular $http, вам нужно будет добавить $scope. $Apply.
Ответ 5
Как отметил @gruberb в комментариях, если вы попытались высмеять вызов REST, лучше использовать обещание, чем $apply
.
Для этого вам нужно использовать услугу $q для создания и возврата обещания. Затем просто вызовите его и выполните работу с вами, вызвав метод then()
по возвращенному обещанию.
function MyCtrl($scope, $q) {
$scope.items = [{name : "abc"},{name : "xyz"},{name : "cde"}];
$scope.change = function(){
test().then(function (items) {
$scope.items = items;
$scope.$apply();
});
};
function test() {
var defered = $q.defer();
var testItem = [
{name : "mno"},
{name : "pqr"},
{name : "ste"}
];
setTimeout(function() {
defered.resolve(testItem);
},2000);
return defered.promise;
}
}
Ответ 6
Все приведенные выше ответы дают некоторую информацию, но они не отвечали на некоторые сомнения, которые у меня были или, по крайней мере, я не понимал. Поэтому я даю свое.
В angular docs четко указано, когда использовать $apply
обратные вызовы $http или $timeout или ng-click, ng -..... имеют $apply(), завернутые в них. Поэтому, когда люди говорят, что вам не нужно использовать $apply(), когда вы делаете angular способ сделать что-то, вот оно. Однако в одном из ответов упоминаются обработчики событий angular, также завернутые с помощью $apply(). Это неверно или тем, что пользователь означает просто ng-click вид событий (снова ng -....). Если событие транслируется на rootScope (или любой области), за пределами $http или $timeout или ng-click, например, например, из пользовательской службы, вам нужно использовать $apply() в своей области, хотя $rootScope. $broadcast также является angular способом делать вещи. в большинстве сценариев нам это не понадобится, потому что состояние приложения меняется, когда что-то происходит. т.е. клики, изменение выбора и т.д.... и они в терминах angular уже используют $apply(), когда мы используем ng-click ng-change соответственно. Обработка событий на стороне сервера с помощью signalr или socket.io и при написании пользовательских директив, где необходимость изменять только область действия директивы - это несколько примеров, когда очень важно использовать $apply()
Ответ 7
Лучше использовать $scope.$applyAsync();
вместо $scope.$apply();
Причина, по которой следует избегать использования $ scope. $ Apply(), указана здесь:
Ошибка: выполняется дайджест [$ rootScope: inprog]. исправлять