在jsfiddle控制台中,您将看到无限的摘要循环.
基本上我必须对可能尚未加载的数据做出决定,所以我需要等待使用then()解析的承诺.我有一个叫做用户的承诺.代码中有两个不同的地方,我在用户上调用then().
>就在我定义它之后.我需要根据它设置范围变量.
>在另一个范围方法中,$scope.isAdmin()
对于数字2,可能会问为什么我不直接在$scope.isAdmin()方法中使用$scope.user.问题是,在用户的异步请求返回之前,可以调用$scope.isAdmin(),在这种情况下,我需要在从$scope.isAdmin()返回之前“阻塞”.
我的问题是,$scope.isAdmin()是什么让角度认为“观察”变量已经改变并且摘要周期需要再次运行?
$scope.isAdmin()实际上并没有改变任何东西.
这是精简代码:
HTML:
<body ng-controller='myController'> <div ng-if='isAdmin()'>Hi! <strong>{{ user.username }}</strong> is an Admin!!!</div> <div ng-if='!isAdmin()'>Hi! <strong>{{ user.username }}</strong> is NOT an Admin!!!</div> </body>
和JS:
angular.module('myApp',[]) .factory('myService',function($q,$timeout) { return { getUser: function() { var deferred = $q.defer(); $timeout(function() { deferred.resolve({ username: 'me',isAdmin: true }); },2000); return deferred.promise; } }; }) .controller('myController',function($scope,$q,myService) { var getUserDeferred = $q.defer(); var user = getUserDeferred.promise; user.then(function(user) { $scope.user = user; return user; }); $scope.getUser = function() { return myService.getUser().then(function(user) { getUserDeferred.resolve(user); }); }; $scope.isAdmin = function() { return user.then(function(user) { return user.isAdmin; }); }; $scope.getUser(); });
解决方法
修复的关键在于两个概念:角度承诺和角度手表.通过了解并应用这两个概念,修复实际上非常简单.
你在$scope上放置的所有东西都是’看过’包括功能.每次观看的内容都会更改$scope.$apply()会再次运行以应用更改.如果范围函数(例如:$scope.isAdmin())将其返回值从“应用”更改为下一个,则会触发另一个“应用”,直到事情稳定并且返回值不变.
但是在我的代码中,我返回了user.then(…),它只返回一个新的promise(由于返回值不断变化,因此使应用周期保持不变).
在我的isAdmin()函数中,我需要推迟其返回值,直到用户实际加载(任何其他返回值都没有意义).所以我更改了代码以检查用户异步调用是否已通过检查$scope.user解决,如果是,则返回有效的isAdmin值.如果$scope.user仍未定义,我只会返回我已经创建的promise.
我将$scope.isAdmin()更改为:
$scope.isAdmin = function() { if ($scope.user) { return $scope.user.isAdmin; } return user; };
这与原始代码具有相同的效果,而不会触发无限应用循环.具体来说,如果$scope.user尚未解决,我们仍然会像以前一样返回一个promise,方法是返回用户var.但请注意,用户var是相同的承诺,而不是then()创建的新承诺,因此应用周期稳定.
只是为了完整性,这里是更新的jsfiddle: