Угловая вкладка

У меня есть страница с некоторыми вкладками, и на каждой вкладке имеется большое количество привязок к угловому. Это - образец страницы, на которой я сталкиваюсь с проблемой. Каждая вкладка занимает около 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>