resolve
的 $routeProvider
属性允许在呈现相应视图之前执行某些作业。
如果我想在执行这些作业时显示微调器以增加用户体验,该怎么办? 实际上,否则用户会感觉应用程序已被阻止,因为在几毫秒内没有显示任何视图元素。例如600。
当然,由于div
功能,有一种方法可以显示当前视图中的全局$scope.$rootChangeStart
元素 以显示微调器。
但我不想隐藏整个页面,中间只有一个可怜的微调器。
我希望我的webapp的某些页面在加载显示方式上有所不同。
我遇到了this interesting post,其中包含我上面描述的确切问题:
这种方法会导致糟糕的UI体验。用户点击 一个刷新列表或按钮的按钮,整个屏幕都可以 在一个普通的微调器中覆盖,因为库没有办法 仅针对实际受影响的视图显示微调器 国家改变。不,谢谢。
无论如何,在我提交这个问题后,我意识到"解决" 功能是一种反模式。它等待所有承诺解决 然后动画状态变化。这完全错了 - 你想要的 状态之间的过渡动画与数据并行运行 加载,以便后者可以被前者掩盖。
例如,假设您有一个项目列表,并单击其中一个 他们隐藏列表并在不同的视图中显示项目的详细信息。 如果我们对项目详细信息进行异步加载,平均而言, 400ms,那么我们几乎可以在大多数情况下完全掩盖负载 有一个300毫秒"离开"列表视图上的动画,以及300毫秒"输入" 项目详细信息视图上的动画。这样我们就能提供更加流畅的感觉 在大多数情况下,可以避免显示微调器。
但是,这要求我们启动异步加载和状态 在同一时刻改变动画。如果我们使用"解决",那么 整个异步动画在动画开始之前发生。用户 点击,看到一个微调器,然后看到过渡动画。整体 状态变化将需要~1000ms,这太慢了。
"解析"可能是缓存之间依赖关系的有用方法 不同的意见,如果它有选择不等待承诺,但是 当前的行为,总是在状态改变之前解决它们 开始让它几乎没用,IMO。对任何人都应该避免 涉及异步加载的依赖项。
我真的应该停止使用resolve
加载一些数据,而是直接开始在相应的控制器中加载它们吗?这样我就可以在作业执行时以及在视图中我想要的位置更新相应的视图,而不是全局。
答案 0 :(得分:51)
您可以使用侦听$routeChangeStart
的指令,例如在触发时显示该元素:
app.directive('showDuringResolve', function($rootScope) {
return {
link: function(scope, element) {
element.addClass('ng-hide');
var unregister = $rootScope.$on('$routeChangeStart', function() {
element.removeClass('ng-hide');
});
scope.$on('$destroy', unregister);
}
};
});
然后将其放在特定视图的加载器上,例如:
查看1:
<div show-during-resolve class="alert alert-info">
<strong>Loading.</strong>
Please hold.
</div>
观看2:
<span show-during-resolve class="glyphicon glyphicon-refresh"></span>
此解决方案(以及许多其他解决方案)的问题在于,如果您从外部站点浏览到其中一条路径,则不会加载先前的ng-view模板,因此您的页面可能只是空白解决。
这可以通过创建一个充当后备加载器的指令来解决。它将侦听$routeChangeStart
并仅在没有先前路由的情况下显示加载程序。
一个基本的例子:
app.directive('resolveLoader', function($rootScope, $timeout) {
return {
restrict: 'E',
replace: true,
template: '<div class="alert alert-success ng-hide"><strong>Welcome!</strong> Content is loading, please hold.</div>',
link: function(scope, element) {
$rootScope.$on('$routeChangeStart', function(event, currentRoute, previousRoute) {
if (previousRoute) return;
$timeout(function() {
element.removeClass('ng-hide');
});
});
$rootScope.$on('$routeChangeSuccess', function() {
element.addClass('ng-hide');
});
}
};
});
使用ng-view:
将后备加载器放置在元素外部<body>
<resolve-loader></resolve-loader>
<div ng-view class="fadein"></div>
</body>
答案 1 :(得分:29)
我觉得这很漂亮
app.run(['$rootScope', '$state',function($rootScope, $state){
$rootScope.$on('$stateChangeStart',function(){
$rootScope.stateIsLoading = true;
});
$rootScope.$on('$stateChangeSuccess',function(){
$rootScope.stateIsLoading = false;
});
}]);
然后在视图
<div ng-show='stateIsLoading'>
<strong>Loading.</strong>
</div>
答案 2 :(得分:11)
为了进一步了解Pranay的回答,我就是这样做的。
JS:
app.run(['$rootScope',function($rootScope){
$rootScope.stateIsLoading = false;
$rootScope.$on('$routeChangeStart', function() {
$rootScope.stateIsLoading = true;
});
$rootScope.$on('$routeChangeSuccess', function() {
$rootScope.stateIsLoading = false;
});
$rootScope.$on('$routeChangeError', function() {
//catch error
});
}]);
HTML
<section ng-if="!stateIsLoading" ng-view></section>
<section ng-if="stateIsLoading">Loading...</section>
答案 3 :(得分:3)
我已经迟到了两年,是的,这些其他解决方案有效,但我发现在一个像这样的运行块中处理所有这些更容易
.run(['$rootScope','$ionicLoading', function ($rootScope,$ionicLoading){
$rootScope.$on('loading:show', function () {
$ionicLoading.show({
template:'Please wait..'
})
});
$rootScope.$on('loading:hide', function () {
$ionicLoading.hide();
});
$rootScope.$on('$stateChangeStart', function () {
console.log('please wait...');
$rootScope.$broadcast('loading:show');
});
$rootScope.$on('$stateChangeSuccess', function () {
console.log('done');
$rootScope.$broadcast('loading:hide');
});
}])
你不需要任何其他东西。很简单吧。这是example的实际行动。
答案 4 :(得分:1)
在ui-router 1.0中,$ stateChange *事件已被弃用。请改用转换钩子。有关详细信息,请参阅下面的迁移指南。
https://ui-router.github.io/guide/ng1/migrate-to-1_0#state-change-events
答案 5 :(得分:0)
当您返回上一个状态时,'$ stateChangeStart'和'$ stateChangeSuccess'的问题是“$ rootScope.stateIsLoading”不会刷新。 对此有什么解决方案吗? 我也用过:
$rootScope.$on('$viewContentLoading',
function(event){....});
和
$rootScope.$on('$viewContentLoaded',
function(event){....});
但是存在同样的问题。