Когда безопасно использовать $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()