0

我想用一些 jQuery ajax 调用返回的数据更新 Angular 范围。我想从 Angular 外部调用 Ajax 的原因是 a) 我希望调用尽快返回,因此它甚至应该在 document.ready 之前开始 b) 有多个调用为多个初始化一个复杂模型-页面网络应用程序;这些调用之间存在依赖关系,我不想在多个 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 发生变化时,这段代码会更新这两个数组。现在是困难的部分。我尝试以两种方式更新 $scope.character :

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错误。第二个版本使用我编写的服务:

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/ 这是我的代码的简化版本。有趣的是,当 deferred 被解决时,它永远不会触发 $watch。

4

1 回答 1

1

It is possible to check whether or not it is safe to call $apply by checking $scope.$$phase. If it returns something truthy--e.g. '$apply' or '$digest'--wrapping your code in the $apply call will result in that error message.

Personally I would go with your second approach, but use the $q service--AngularJS's promise implementation.

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

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

      return deferred.promise;
    }
  };
});

Since AngularJS has native support for this promise implementation it means you can change your code to:

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

When the AJAX call is done, the promise is resolved and AngularJS will automatically take care of the bindings, trigger the $watch etc.

Edit after fiddle was added

Since the jQuery promise is used inside the service, Angular has no way of knowing when that promise is resolved. To fix it you need to wrap the resolve in an $apply call. Updated fiddle. This solves the fiddle, I hope it solves your real problem too.

于 2013-01-26T17:06:49.010 回答