javascript Angularjs 选项卡在渲染时加载微调器
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/30936653/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Angularjs tab Loading spinner while rendering
提问by Navaneet
I have a page with some tabs and each tab has large amount of angularjs bindings.Thisis sample page where i am facing issue.Each tabs take about 10 seconds to render.
我有一个带有一些选项卡的页面,每个选项卡都有大量的 angularjs 绑定。这是我遇到问题的示例页面。每个选项卡大约需要 10 秒来呈现。
So i planned to give a loading spinner while tab renders.
So i planned to show loading spinner during click on the tab and remove the spinner at the end($last
) of the ng-repeat.
所以我计划在选项卡渲染时提供一个加载微调器。因此,我计划在单击选项卡时显示加载微调器,并$last
在 ng-repeat的末尾()移除微调器。
In the ng-click on tab i activated the spinning loader
在 ng-click 选项卡中,我激活了旋转加载器
<ul>
<li ng-repeat="tab in tabs"
ng-class="{active:isActiveTab(tab.url)}"
ng-click="onClickTab(tab)">{{tab.title}}
</li>
</ul>
In controller
在控制器中
$scope.onClickTab = function (tab) {
showLoader();
$scope.currentTab = tab.url;
}
To check ng-repeat is complete i have used below directive
为了检查 ng-repeat 是否完整,我使用了以下指令
.directive('onFinishRender', function ($timeout) {
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true) {
$timeout(function () {
scope.$emit('ngRepeatFinished');
});
}
}
}
});
$scope.$on('ngRepeatFinished', function (ngRepeatFinishedEvent) {
removeLoader();
});
showLoader and removeLoader are simple function which append and remove the div having a simple loading spinner.
showLoader 和 removeLoader 是简单的函数,它们附加和删除具有简单加载微调器的 div。
function showLoader() {
$('body').append('<div class="loader"></div>');
}
function removeLoader() {
$('.loader').fadeOut(function () {
$(this).remove();
});
}
Expected result: Spinning loader to be shown when clicked on tab and appear till ng-repeat finishes.(i.e the clicked tab renders completely)
预期结果:单击选项卡时将显示旋转加载程序并出现直到 ng-repeat 完成。(即单击的选项卡完全呈现)
Actual result: The loader is not shown when clicked on tab and it appear almost at the end of ng-repaet and appear for a fraction of seconds. Hereyou can observe the said behavior. I think the page is not able to show the spinner due to the angular bindings process which makes page freeze.
实际结果:单击选项卡时未显示加载程序,它几乎出现在 ng-repaet 的末尾并出现几秒钟。在这里您可以观察到上述行为。我认为由于角度绑定过程导致页面冻结,页面无法显示微调器。
Can anyone help me to resolve this?
谁能帮我解决这个问题?
采纳答案by Umut Benzer
You can change your code like this:
您可以像这样更改代码:
$timeout(function() {
$scope.currentTab = tab.url
}, 100);
Demo: http://jsfiddle.net/eRGT8/1053/
演示:http: //jsfiddle.net/eRGT8/1053/
What I do is, I push currentTab
change to next digest cycle. (It's some kind of a hack, not a solution I proud of.)Therefore, in first digest cycle (before $timeout
invoked) only loading is shown. In the next digest cycle, the blocking ng-repeat
stuff starts working but since we make loading visible previously, it appears.
我所做的是,将currentTab
更改推送到下一个摘要周期。(这是某种hack,不是我引以为豪的解决方案。)因此,在第一个摘要循环($timeout
调用之前)中只显示加载。在下一个摘要循环中,阻塞的ng-repeat
东西开始工作,但由于我们之前使加载可见,它出现了。
:)
:)
This solves your problem, but running long running and blocking javascript that hangs browser completely is not a good user experience. Also, since browser is hang completely, the loading gif will not animate, only will be visible.
这解决了您的问题,但是长时间运行并阻止完全挂起浏览器的 javascript 并不是一个好的用户体验。此外,由于浏览器完全挂起,加载 gif 不会动画,只会可见。
回答by Alpha G33k
I would STRONGLY recommend reading this SO thread first, written by the author of AngularJS himself which addresses the root problem.
我强烈建议先阅读这个 SO 线程,它是由 AngularJS 的作者自己编写的,它解决了根本问题。
Delaying AngularJS route change until model loaded to prevent flicker
Misko asks then answers his own question regarding this topic by suggesting the use of $routeProvider, like so:
Misko 然后通过建议使用 $routeProvider 来回答他自己关于这个主题的问题,如下所示:
$routeProvider.
when('/phones', {
templateUrl: 'partials/phone-list.html',
controller: PhoneListCtrl,
resolve: PhoneListCtrl.resolve}). // <-- this is the connection when resolved
when('/phones/:phoneId', {
templateUrl: 'partials/phone-detail.html',
controller: PhoneDetailCtrl,
resolve: PhoneDetailCtrl.resolve}).
otherwise({redirectTo: '/phones'});
Here is the finished product/solution you are seeking, which is an enhanced version of the AngularJS phone demo:
这是您正在寻求的成品/解决方案,它是 AngularJS 手机演示的增强版:
http://mhevery.github.io/angular-phonecat/app/#/phones
http://mheevery.github.io/angular-phonecat/app/#/phones
This requires NgRoute(), but is the correct approach. Using a $timeout here seems kind of hacky to me - Im not saying you need ngRoute however I am not sure how to pull it off without it (and I DO realize that your tabs may not be routed currently but I hope it helps anyways).
这需要 NgRoute(),但这是正确的方法。在这里使用 $timeout 对我来说似乎有点 hacky - 我不是说你需要 ngRoute 但是我不知道如何在没有它的情况下完成它(我确实意识到你的标签目前可能没有被路由,但我希望它无论如何都会有所帮助) .
Furthermore, may also find using angular.element(document).ready(function(){}); for your initial payload will also solve your woahs.
此外,还可以找到使用 angular.element(document).ready(function(){}); 因为您的初始有效载荷也将解决您的问题。
angular.element(document).ready(function(){
$('.loading').remove(); // Just an example dont modify the dom outside of a directive!
alert('Loaded!');
});
As you can see no $timeout(function(){}); is used either approach, here is proof of the angular.element.ready() works - without a router:
如您所见,没有 $timeout(function(){}); 使用任何一种方法,这里是 angular.element.ready() 工作的证明 - 没有路由器:
WORKING DEMOhttp://codepen.io/nicholasabrams/pen/MwOMNR
工作演示http://codepen.io/nicholasabrams/pen/MwOMNR
*if you reaaally need me to I can make some tabs up but you didn't give a working code sample in your post so I used another demo I made for another thread to show the solution.
*如果你真的需要我,我可以制作一些标签,但你没有在你的帖子中提供工作代码示例,所以我使用了我为另一个线程制作的另一个演示来展示解决方案。
HERE IS YET ANOTHER SOLUTION, this time I used your example!
这是另一种解决方案,这次我使用了您的示例!
http://jsfiddle.net/eRGT8/1069/
http://jsfiddle.net/eRGT8/1069/
This time I fire a function when the $index === $last (last element in ngRepeat()) this way the loader is removed from the dom or hidden when the repeat is finished doing its business)
这次我在 $index === $last (ngRepeat() 中的最后一个元素)时触发一个函数,这样加载器从 dom 中删除或在重复完成其业务时隐藏)
回答by Carsten
I usually solve this in my HTML using ng-if.
我通常使用 ng-if 在我的 HTML 中解决这个问题。
In your case this would become something like:
在您的情况下,这将变成:
<ul>
<span ng-if="!tabs">Content loading...</span>
<span ng-if="tabs && tabs.length < 1">No tabs available</span>
<span ng-if="tabs && tabs.length > 0">
<li ng-repeat="tab in tabs"
ng-class="{active:isActiveTab(tab.url)}"
ng-click="onClickTab(tab)">{{tab.title}}
</li>
</span>
</ul>
回答by Mikalai
I think that it's not posibble to show loading spinner while rendring the page. The rendering engine is single threaded. Almost everything, except network operations, happens in a single thread. In Firefox and safari this is the main thread of the browser. In chrome it's the tab process main thread. Network operations can be performed by several parallel threads.
我认为在渲染页面时显示加载微调器是不可能的。渲染引擎是单线程的。除了网络操作之外,几乎所有事情都发生在单个线程中。在 Firefox 和 safari 中,这是浏览器的主线程。在 chrome 中,它是选项卡进程主线程。网络操作可以由多个并行线程执行。
You can read aboout that here
你可以在这里阅读
There is only one way to solve this problem - to redesign the page: to show only part of data, lazy loading or etc.
只有一种方法可以解决这个问题——重新设计页面:只显示部分数据、延迟加载等。
回答by Milos Mosovsky
You can make custom directive for each li example loading-counter , then add dependency service which will hold counter, and in each directive inside link function you can call
您可以为每个 li 示例 loading-counter 制作自定义指令,然后添加将保存计数器的依赖服务,并且在链接函数内的每个指令中,您可以调用
var app = angular.module('app', []);
app.controller('List', function(LoadingCounter) {
this.tabs = [{
title: "Test tab",
url: "http://google.sk"
}, {
title: "Test tab",
url: "http://google.sk"
}, {
title: "Test tab",
url: "http://google.sk"
}]
this.LoadingCounter = LoadingCounter;
});
app.directive('spinningLoader', function(LoadingCounter) {
return {
restrict: 'A',
scope: {
max: "="
},
link: function(scope) {
LoadingCounter.updateCounter(scope.max);
}
}
});
app.factory('LoadingCounter', function($timeout) {
var service = {};
service.currentCounter = 0;
service.finished = false;
service.updateCounter = function(max) {
service.currentCounter++;
if (service.currentCounter == max) {
//example timeout
$timeout(function() {
service.finished = true;
}, 2000);
}
}
service.isFinished = function() {
return service.finished;
}
return service;
});
.hidden {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="app" ng-controller="List as _">
<div ng-if="!_.LoadingCounter.isFinished()">Loading</div>
<ul ng-class="{'hidden' : !_.LoadingCounter.isFinished() }">
<li spinning-loader max="_.tabs.length" ng-repeat="tab in _.tabs" ng-class="{active:isActiveTab(tab.url)}" ng-click="onClickTab(tab)">{{tab.title}}
</li>
</ul>
</body>