title | categories | tags | date | keywords | ||
---|---|---|---|---|---|---|
【Angular】给 $http 加上好用的操作提示indicator interceptor |
技术-前端开发 |
|
2015-09-11 00:56:29 -0700 |
Nodejs, 前端, ES6 |
用户在使用你的产品需要及时的反馈,譬如在拉取后端数据时候,在做了某些涉及后端的操作时,在消费数据时遇到某些莫名错误时。 同时,我们在开发的时候,如果对于这些通用的操作提示能够有全局的一致的UI/UX的话,可以继承进入 Angular 的$http 模块中,这样不需要在每次业务编写时都手动改下。
所以下面的 Http interceptor 主要解决如下三个问题:
- 数据拉取的模态 loading
- POST/PUT/DELETE 等 操作button的buzy 和点击屏蔽disabled button
- 通用的5xx的报错提示等
提交indicator | 错误indicator | 拉取indicator |
---|---|---|
$api('getFundRatio', {
pageSize: 60,
pageNum: 1
}, {
// global 代表全局loading indicator
indicator: 'global'
});
ng-click="depositFund($event)"
$api('depositFund', {
amount: vm.amount,
bankcode: vm.currentCard.bankcode,
bankno: vm.currentCard.typeno,
mobilephone: vm.mobile
}, {
// 忽略通用的错误踢死
ignoreErr: true,
// 传入 button 来做按钮提示和在ajax过程中的disabled
indicator: e.target
}).then((r)=>{
$temp.set('charge', {
card: vm.currentCard,
amount: vm.amount,
dealDate: vm.dealDate
});
$state.go('moneyio-charge-succ');
}, (r)=>{
// 自定义的错误处理
$temp.set('charge', r.data);
$state.go('moneyio-charge-error');
});
'use strict';
// ngInject
var indicator = ($injector, $timeout, $q, $rootScope) => {
var $ionicLoading, $notice, $compile;
var toggleGlobalIndicator = tplMethod(()=> {
$ionicLoading.show({
template: '<ion-spinner icon="ios"></ion-spinner>',
animation: 'fade-in',
showBackdrop: true // 通过黑透明遮罩,来模态
// showDelay: 100
});
}, ()=>{
$ionicLoading.hide()
}, 800);
var toggleBtnIndicator = tplMethod((target)=>{
$compile = $injector.get('$compile');
// avoid chain indicator
if($(target).find('ion-spinner').length) return;
$(target).prepend(
$compile('<ion-spinner icon="ios-small"></ion-spinner>'
)($rootScope));
target.disabled = true;
}, (target)=>{
if(!target) return;
target.disabled = false;
$(target).find('ion-spinner').remove();
}, 3000); // 至少3s的显示
function handleRequest(conf) {
if(!_.isUndefined(conf.indicator)) {
conf.timeout = 10000; // default timeout
}
if(!conf.indicator) return;
conf.indicator === 'global'
? toggleGlobalIndicator(true)
: toggleBtnIndicator(true, conf.indicator);
// whether post/delete auto set global indicator
// check conf.method POST DELETE
}
function handleResponse(r) {
if (!r.config) return;
if(_.isUndefined(r.config.indicator)) return;
r.config.indicator === 'global'
? toggleGlobalIndicator(false)
: toggleBtnIndicator(false, r.config.indicator);
}
return {
request: function(conf) {
// $ionicLoading 依赖需要动态的插入,避免循环引用
$ionicLoading = $injector.get('$ionicLoading');
handleRequest(conf);
return conf || $q.when(conf);
},
response: function(response) {
$ionicLoading = $injector.get('$ionicLoading');
handleResponse(response);
return response;
},
responseError: function(rejection) {
handleResponse(rejection);
// check rejection.config.ignoreErr
if(rejection.status === 403) {
$rootScope.$storage.userData = null;
// auto redirect to login?!
}
if(rejection.data && !_.isUndefined(rejection.data.error)) {
// ignoreErr 来允许HTTP API 使用方直接定制错误处理
if(!rejection.config.ignoreErr) {
$notice = $injector.get('$notice');
$timeout(()=>{
$notice.error(rejection.data.error);
}, 100);
}
}
return $q.reject(rejection);
}
}
function tplMethod(yes, no, span) {
var time, delta
return function(bool) {
var args = _.toArray(arguments)
args.shift();
if(bool) {
time = new Date().getTime();
yes.apply(null, args);
} else {
delta = new Date().getTime() - time;
$timeout(()=>{
no.apply(null, args); // why not Fn.bind
}, 0); // span - delta
}
}
}
};
export default indicator;