仅记录下最近学到的一点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定义
routeProvider的resolve可以设置Object.<string,function>=,这个map里面的依赖可以被注入到控制器中。当其中一个依赖是promise时,根据官方资料[ routeProvider](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider) 所说,router 会等到里面所有的promise 对象都 成功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的初始化时间。
有更好的方法的话,希望可以留言教一下我咯~