web-dev-qa-db-ja.com

$ 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);
  }
}
15
Zee

API-Rest-Callを強制したい場合は、Rest-Call内にスコープを設定する代わりに、返されたpromiseControllerで使用します。

_$http.get('uri')
  .success(function(data) {
    $scope.items = data
});
_

$apply()の使用は避けてください。 Angular GitHub Repo から:

$scope.$apply()は、非同期イベントバインディングのできるだけ近くで発生する必要があります。

コード全体にランダムに振りかけないでください。実行している場合、_(!$scope.$$phase) $scope.$apply()_の場合は、コールスタックで十分な高さにないためです。

あなたの質問に:

  • $ apply()が必要な状況に陥った場合は、構造を考え直してください。
  • 安全上の理由のみ:$apply()は使用しないでください
5
Bastian Gruber

Anzeoが$ timeoutについて語ったように、「角度のある方法」ではない何かを使用するたびに$ applyを使用する必要があります。
たとえば、angularの$ httpの代わりにjQueryのhttpを使用する場合、$ scope。$ applyを追加する必要があります。

3
Ivan Toncev

$ apply は、コードがangularダイジェストループで実行されない場合に使用する必要があります。通常の状況では使用する必要はありませんが、 jQueryイベントハンドラーまたはsetTimeout()などのメソッドから呼び出されるコードがある場合に使用します。別のangular functionから呼び出される関数がある場合でもwatchまたはangularイベントハンドラーのように、これらのスクリプトはダイジェストサイクルで実行されるため、$ apply()を使用する必要はありません。

安全な方法の1つは、$scope.$$phaseパラメータを呼び出す前に $ scope。$ apply() のように

if($scope.$$phase){
    $scope.$apply();
}

あなたの場合では、別の答えで提案されているように$ timeoutを使用できます

3
Arun P Johny

より良い方法は、$scope.$applyAsync();の代わりに$scope.$apply();を使用することです

$ scope。$ apply()の使用を避ける理由は次のとおりです。

エラー:[$ rootScope:inprog]ダイジェストが進行中。修正

0

上記のすべての答えはいくつかの情報を提供しますが、それらは私が持っていた、または少なくとも私が理解しなかったいくつかの疑問には答えませんでした。だから私は自分のものを与えています。

angular docsに非常に明確に記載されています $ applyを使用する場合

$ httpまたは$ timeoutまたはng-click、ng -.....のコールバックには、$ apply()がラップされています。したがって、angularの方法を実行するときに$ apply()を使用する必要がないと人々が言うとき、これはそれです。ただし、回答の1つでangularイベントハンドラーについても$ apply()でラップされています。これは当てはまりません。つまり、ユーザーはng-click種類のイベント(これもng -....)を意味します。イベントが$ httpまたは$ timeoutまたはng-clickの外でrootScope(またはその問題の任意のスコープ)でブロードキャストされる場合(例:カスタムサービスから)、$ rootScopeであってもスコープで$ apply()を使用する必要があります。 。$ broadcastは、angularの方法でもあります。ほとんどのシナリオでは、何かが発生するとアプリの状態が変化するため、これは必要ありません。つまり、クリック、選択の変更などです。これらはangularにあり、それぞれng-click ng-changeを使用するときに$ apply()を使用しています。 signalrまたはsocket.ioを使用して、またディレクティブのスコープのみを変更する必要があるカスタムディレクティブを記述しながら、サーバー側イベントを処理する例は、$ apply()を使用することが非常に重要ないくつかの例です。

@gruberbがコメントで指摘したように、REST呼び出しをモックしようとした場合は、_$apply_よりもプロミスを使用する方が適切です。

そのためには、 $ q service を使用してpromiseを作成して返す必要があります。次に、それを呼び出し、返されたpromiseで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;
    }
}
_
0
Blackus