Угловая вкладка
У меня есть страница с некоторыми вкладками, и на каждой вкладке имеется большое количество привязок к угловому. Это - образец страницы, на которой я сталкиваюсь с проблемой. Каждая вкладка занимает около 10 секунд визуализировать.
Итак, я планировал дать загрузочный счетчик во время отображения вкладки.
Поэтому я планировал показать загрузочный счетчик во время щелчка по вкладке и удалить счетчик в конце ($last
) ng-repeat.
На вкладке ng-click на вкладке я активирован вращающийся загрузчик
<ul>
<li ng-repeat="tab in tabs"
ng-class="{active:isActiveTab(tab.url)}"
ng-click="onClickTab(tab)">{{tab.title}}
</li>
</ul>
В контроллере
$scope.onClickTab = function (tab) {
showLoader();
$scope.currentTab = tab.url;
}
Чтобы проверить, что 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 и removeLoader - это простая функция, которая добавляет и удаляет div, имеющий простой загрузчик.
function showLoader() {
$('body').append('<div class="loader"></div>');
}
function removeLoader() {
$('.loader').fadeOut(function () {
$(this).remove();
});
}
Ожидаемый результат: Спиннинг-загрузчик будет отображаться при нажатии на вкладку и появляться до тех пор, пока ng-repeat не завершится. (т.е. щелкнув вкладку полностью)
Фактический результат: загрузчик не отображается при нажатии на вкладку и появляется почти в конце ng-repaet и отображается в течение нескольких секунд. Здесь вы можете наблюдать указанное поведение. Я думаю, что страница не может показать spinner из-за процесса привязки angular, который заставляет замораживать страницу.
Может ли кто-нибудь помочь мне разрешить это?
Ответы
Ответ 1
Вы можете изменить свой код следующим образом:
$timeout(function() {
$scope.currentTab = tab.url
}, 100);
Демо: http://jsfiddle.net/eRGT8/1053/
Что я делаю, я нажимаю currentTab
на следующий цикл дайджеста. (Это какой-то хак, а не решение, которому я горжусь.) Поэтому в первом цикле дайджеста (до вызова $timeout
) отображается только загрузка. В следующем цикле дайджест начинает работать блокирующий элемент ng-repeat
, но, поскольку мы делаем загрузку видимым ранее, он появляется.
:)
Это решает вашу проблему, но выполнение длительной работы и блокировка javascript, полностью зависающего от браузера, не является хорошим пользовательским интерфейсом. Кроме того, поскольку браузер полностью зависает, загрузка gif не будет анимироваться, только будет видна.
Ответ 2
Я бы настоятельно рекомендовал сначала прочитать этот SO-поток, написанный автором самого AngularJS, который решает проблему с корнем.
Отмена изменения маршрута AngularJS до загрузки модели для предотвращения мерцания
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'});
Вот готовый продукт/решение, которое вы ищете, это расширенная версия демонстрации телефона AngularJS:
http://mhevery.github.io/angular-phonecat/app/#/phones
Для этого требуется NgRoute(), но это правильный подход. Использование $timeout здесь кажется взломанным для меня - я не говорю, что вам нужен ngRoute, но я не уверен, как его вытащить без него (и я понимаю, что ваши вкладки не могут быть перенаправлены в настоящее время, но я надеюсь, что это поможет в любом случае).
Кроме того, также можно найти с помощью angular.element(document).ready(function() {}); для вашей начальной полезной нагрузки также будут решаться ваши woahs.
angular.element(document).ready(function(){
$('.loading').remove(); // Just an example dont modify the dom outside of a directive!
alert('Loaded!');
});
Как вы можете видеть нет $timeout (function() {}); используется либо подход, вот доказательство работы angular.element.ready() - без маршрутизатора:
РАБОЧИЙ ДЕМО
http://codepen.io/nicholasabrams/pen/MwOMNR
* Если вы мне в этом нуждаетесь, я могу сделать несколько вкладок, но вы не дали пример рабочего кода в своем сообщении, поэтому я использовал еще одну демонстрацию, которую я сделал для другого потока, чтобы показать решение.
ЗДЕСЬ ЕЩЕ ДРУГОЕ РЕШЕНИЕ, на этот раз я использовал ваш пример!
http://jsfiddle.net/eRGT8/1069/
На этот раз я запускаю функцию, когда $index === $last (последний элемент в ngRepeat()) таким образом, что загрузчик удаляется из dom или скрывается, когда повтор завершается, делая свой бизнес)
Ответ 3
Я обычно решаю это в своем HTML, используя ng-if.
В вашем случае это станет чем-то вроде:
<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>
Ответ 4
Я думаю, что он не может показывать загружаемую прядильщик при перелистывании страницы. Механизм рендеринга имеет одну резьбу. Почти все, кроме сетевых операций, происходит в одном потоке. В Firefox и сафари это основной поток браузера. В хроме это основной процесс обработки вкладок.
Сетевые операции могут выполняться несколькими параллельными потоками.
Вы можете читать aboout здесь
Есть только один способ решить эту проблему - переделать страницу: отображать только часть данных, ленивую загрузку и т.д.
Ответ 5
Вы можете создать настраиваемую директиву для каждого примера загрузки экземпляра li, затем добавить службу зависимостей, которая будет содержать счетчик, а в каждой директиве внутри функции ссылок вы можете позвонить
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>