0

jQuery ajax 呼び出しによって返されたデータで Angular スコープを更新したいと考えています。Angular の外部から Ajax を呼び出したい理由は、a) 呼び出しをできるだけ早く返したいので、document.ready の前でも開始する必要があるためです b) 複数の複雑なモデルを初期化する呼び出しが複数あるためです-ページ Web アプリ; 呼び出しには相互に依存関係があり、複数の Angular コントローラーでロジックを複製したくありません。

これはコントローラーからのコードです。ここに収まるように、コードはいくらか単純化されていることに注意してください。

$scope.character = {};
$scope.attributeArray = [];
$scope.skillArray = [];

理由はキャラクターの属性やスキルはオブジェクトとして入ってくるのですが、ng-repeatで表示するので配列として必要だからです。

$scope.$watch('character',function(){
  $scope.attributeArray = getAttributeArray($scope.character);
  $scope.skillArray = getSkillArray($scope.character);
});

理論的には、$scope.character が変更されると、このコードは 2 つの配列を更新します。ここからが難しい部分です。$scope.character を 2 つの方法で更新してみました。

characterRequestNotifier.done(function() { // this is a jQuery deferred object
  $scope.$apply(function(){ // otherwise it's happening outside the Angular world
    $scope.character = CharacterRepository[characterId]; // initialized in the jquery ajax call's return function
  });
});

これにより、$digest is already in progressエラーが発生することがあります。2 番目のバージョンでは、私が作成したサービスを使用します。

repository.getCharacterById($routeParams.characterId, function(character){
  $scope.character = character;
});

、 どこ

.factory('repository', function(){
  return {
    getCharacterById : function(characterId, successFunction){
      characterRequestNotifier.done(function(){
        successFunction( CharacterRepository[characterId] );
      });
    }
  };
});

これは常に $watch をトリガーするとは限りません。

最後に、問題は次のとおりです。このタスクをどのように達成できますか (ソースを特定できないランダムエラーなしで)。私のアプローチに根本的な問題がありますか?

編集:

ここでこの jsfiddle を試してください: http://jsfiddle.net/cKPMy/3/ これは私のコードの簡略化されたバージョンです。興味深いことに、遅延が解決されたときに $watch をトリガーすることはありません。

4

1 に答える 1

1

$apply を呼び出すことが安全かどうかは、 をチェックすることで確認できます$scope.$$phase。それが真実を返す場合、たとえば'$apply'、または'$digest'呼び出しでコードをラップする$applyと、そのエラーメッセージが表示されます。

個人的にはあなたの 2 番目のアプローチを採用しますが、$qAngularJS の promise 実装というサービスを使用します。

.factory('repository', function ($q) {
  return {
    getCharacterById : function (characterId) {
      var deferred = $q.defer();

      characterRequestNotifier.done(function () {
        deferred.resolve(CharacterRepository[characterId]);
      });

      return deferred.promise;
    }
  };
});

AngularJS はこの promise 実装をネイティブにサポートしているため、コードを次のように変更できます。

$scope.character = repository.getCharacterById(characterId);

AJAX 呼び出しが完了すると、promise が解決され、AngularJS が自動的にバインディングを処理し、トリガー$watchなどを行います。

フィドルが追加された後に編集

jQuery promise はサービス内で使用されるため、Angular はその promise がいつ解決されるかを知る方法がありません。$applyそれを修正するには、解決を呼び出しでラップする必要があります。フィドルを更新しました。これでフィドルが解決します。実際の問題も解決することを願っています。

于 2013-01-26T17:06:49.010 に答える