[英]Angular async calls
我的Angular应用程序中发生一个奇怪的问题,我认为Angular的异步性是原因,但我不确定如何解决。
在.run
我有以下内容:
app.run(function ($user, $soundcloud) {
$user.set();
$soundcloud.set();
});
然后是两项服务...
app.service('$user', ['$http', function ($http) {
var currentUser;
this.set = function () {
$http.get('/users/active/profile')
.success(function (obj) {
currentUser = obj;
})
.error(function () {
console.log('ERR');
})
}
this.fetch = function () {
return currentUser;
}
}]);
app.service('$soundcloud', ['$http', function ($http) {
var sc;
this.set = function () {
$http.get('/users/active/services/soundcloud')
.success(function (obj) {
sc = obj;
})
.error(function () {
console.log('Error fetching soundcloud feed')
})
}
this.fetch = function () {
return sc;
}
}]);
我遇到的问题是,当我转到/ profile并重新加载页面时,从$ soundcloud.fetch()返回的数据不可用,除非我将$ soundcloud.fetch()包装在setTimeout()
函数中,这显然是不是很理想,所以我想知道应该在哪里放置$soundcloud.set()
以便在加载页面时立即使这些数据可用。
这是我目前的控制器:
app.controller('profileController', ['$scope', '$user', '$soundcloud', function ($scope, $user, $soundcloud) {
$scope.activeUser = $user.fetch();
$scope.activeSoundcloud = $soundcloud.fetch();
console.log($user.fetch());
setTimeout(function () {
console.log($soundcloud.fetch());
},100);
}]);
如果我将$soundcloud.fetch()
置于setTimeout之外,那么结果将undefined
。 为了使两组数据(用户和soundcloud)立即可用,应该在哪里调用$ soundcloud.set / fetch?
解决此问题的一种方法是获取所有必需的数据,然后手动引导应用程序。
为此,请创建bootstrapper.js
。 您可以通过angular.injector(['ng']).invoke()
来访问内置的角度服务,而无需引导模块,该方法接受具有可注入依赖性的函数。 urlMap
变量只是用于设置及其每个相应URL的键值存储。 您urlMap
通过angular.forEach()
循环此urlMap
,然后一个个地获取所有设置,并将每个设置及其每个键存储在settings
变量中。 将每个$http
promise请求存储在promises
数组中,以使用$q.all()
进行解析。 解决所有承诺后,调用bootstrap()
函数,该函数会在app.settings
模块中添加一个Settings
值,然后手动引导该应用程序。
bootstrapper.js
angular.injector(['ng']).invoke(function($http, $q) {
var urlMap = {
$user: '/users/active/profile',
$soundcloud: '/users/active/services/soundcloud'
};
var settings = {};
var promises = [];
var appConfig = angular.module('app.settings', []);
angular.forEach(urlMap, function(url, key) {
promises.push($http.get(url).success(function(data) {
settings[key] = data;
}));
});
$q.all(promises).then(function() {
bootstrap(settings);
}).catch(function() {
bootstrap();
});
function bootstrap(settings) {
appConfig.value('Settings', settings);
angular.element(document).ready(function() {
angular.bootstrap(document, ['app', 'app.settings']);
});
}
});
app.js
angular.module('app', [])
.run(function(Settings) {
if(Settings === null) {
// go to login page or
// a page that will display an
// error if any of the requested
// settings failed
}
})
.controller('ProfileController', function($scope, Settings) {
console.log(Settings);
});
$ soundcloud.set()的结果将覆盖sc的值。
如果在返回$ soundcloud.set()之前尝试将$ soundcloud.fetch中的值绑定到作用域,则由于未定义var sc,因此它将未定义绑定到作用域。 当$ soundcloud.set()设置sc字段时,scope变量将永远不会得到通知,因为它仅获得未定义的值,而对sc一无所知。
最简单的解决方案是创建一个永远不会删除的包装对象。 这样的事情。
app.service('$soundcloud', ['$http', function ($http) {
var sc = {}; // define an object instead of undefined
this.set = function () {
$http.get('/users/active/services/soundcloud')
.success(function (obj) {
sc.soundcloud = obj; // write to a field on the object,
// do not override sc, since sc is already returned
})
.error(function () {
console.log('Error fetching soundcloud feed')
})
}
this.fetch = function () {
return sc;
}
}]);
使用$ soundcloud.fetch()的结果时,需要引用soundcloud字段。 至少在调用$ soundcloud.set时,它将始终得到更新
您认为这是异步问题是对的。
.run()
的代码触发一个异步.get()
。 .success()
的回调运行之前,请运行fetch()
。 这样, fetch()
返回一个未定义的sc
。 console.log()
看到,因此未定义。 .get()
的数据解析承诺,然后将sc
分配给结果。 @Andre Paap刚才概述的方法是一种很好的方法-本质上是通过创建一个空白引用对象并在回调上更新其属性之一(而不是完全覆盖该引用),您仍然可以将此方法分配给作用域,填充视图后(使用数据绑定)“神奇地”更新视图。 但是 ,如果立即console.log()
结果,您仍然会看到相同的内容。
换句话说,如果您的服务中包含以下内容:
var currentUser = {};
this.set = function () {
return $http.get('/users/active/profile')
.success(function (obj) {
currentUser.name = obj.name;
})
.error(function () {
console.log('ERR');
})
}
这在您的模板中:
<span ng-show="currentUser.name"> Hello {{currentUser.name}}! </span>
然后将其放在您的控制器中将导致上述范围在set()
调用返回后正确显示:
$scope.currentUser = $user.fetch()
// NOTE: the below would still log undefined:
console.log($scope.currentUser.name) //<- undefined.
如果您只想在返回.set()
调用后才运行某些代码,则可以利用$http
方法返回promise的事实,例如:
$user.set().then(function(){
// This code runs *after* the call has returned.
$scope.currentUser = $user.fetch()
console.log($scope.currentUser.name) //<- "Ed"
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.