仅记录下最近学到的一点angular js 知识。
业务描述
前情说明
最近在做一个angularjs的相关项目,需要在page render之前调用后台的restapi 获取到用户名user,用该值去设置某些service并用于前端页面渲染(全部页面渲染之前必须得到user),并且在app.run 中要inject的一些service(例如项目中item.js定义的Item service) 也需要获取到user。
解决方案
要点1 - promises
promises在angularjs中是通过$q来提供的。
REST API 调用封装在Data service中,Data service 是基于\$http的,因此是hardcode了异步的调用。
service定义如下:
angular.module('myApp')
.config(function($httpProvider){
$httpProvider.defaults.headers.common['Content-Type'] = 'application/json;charset=utf-8';
})
.service('Data',function ($q,$http) {
this.getUser() = function(){
return getUrl('xxxx');
};
//define getUrl method
var getUrl = function(url,timeout) {
var defferred = $q().defer;
var opts = {
method:'GET',url:url
};
if(timeout)
opts.timeout = timeout;
$http(opts)
.success(function(result,status,headers){
//do some handling
deferred.resolve(result);
})
.error(function(error){
// do some error handling
deferred.reject(xxx);
});
return deferred.promise;
};//end of getUrl
});//end of service
为了获取a,定义了一个单独的service User in user.js.
angular.module('myApp')
.service('User',function(Data,$rootScope,otherService) {
//this is the default locale.
var DEFAULT_VALUE = 'Guest';
this.getUser = function () {
var promise = Data.getUser().then(
function(result){ // fetch succeed
$rootScope.user = result;
},function(result){ //fetch Failed.
$rootScope.user = DEFAULT_VALUE ;
}
).finally(
function(){
otherService.setxxxx($routScope.user);//if need to set.
}
);
};
});
要点2 - resolve of $routeProvider
promise 解决了获取到值后的回调问题,仍需要其他方法来保证在页面加载前能够完成回调函数的执行。
从各处资料中找到了两种思路:
1) manual bootstrap angular app
2 ) $routeProvider中 set resolve
由于手动bootstrap app受组内原因限制被否,因此最终选择了第二个思路。
resolve定义
global resolve
但是因为router 配置的比较多,每个router都要设置相同的resolve,会比较麻烦,所以通过修改$routeProvider的 when函数定义,来达到简化代码的目的。
angular
.module('myApp',[
'ngRoute'
//....etc
])
.config(function ($routeProvider) {
// can't add service in the argument since config only support provider
//define universal resolve for all routes
var globalResolve = {
'user' : function(User){ // User service as argument
return User.getUser();
}
};
// extend routeProvider to have a global resolve
var originWhen = $routeProvider.when;
$routeProvider.when = function(path,route){
route.resolve = route.resolve || (route.resolve = {});
angular.extend(route.resolve,globalResolve);
return originWhen.call($routeProvider,path,route);
};
$routeProvider
.when('/',{
templateUrl: 'views/main.html',controller: 'MainCtrl'
})
//....etc routers
});
要点3 app.run 和 resolve的 promise回调执行顺序
在项目中有以下定义:
app.run(function (\$rootScope,\$location,Data,Time,\$timeout,Item) {
//do something
});
其中Item service的定义中用到了 user值,因此会出现问题。
不过此处还需要更深的对angularjs service初始化顺序、app.run执行时间、resolve完成时间三者顺序的调研,但是根据此次的观察,app.run 执行可能是在user的值被获取并完成回调处理之前。
目前也没有找到很好的办法来解决这个问题,所幸app.run 要注入的service 只有Item 是依赖user 并且可以从app.run中移走到其他controller里面的,因此最终就是从app.run的注入service列表中移走了Item,推迟了Item的初始化时间。
有更好的方法的话,希望可以留言教一下我咯~