Угловая загрузка бар и загрузка изображений / представлений / состояний

Я использую UI-маршрутизатор для моего углового приложения. У меня есть несколько просмотров, которые содержат изображения. Я бы хотел, чтобы angular-loading-bar также отслеживал окончание загрузки этих изображений. Или, в общем, до тех пор, пока представление не закончило рендеринг.

Когда я наткнулся на этот пост о том, как делать SEO с angularJS, он указал, что мы должны создавать статические html-снимки каждой "страницы" и предоставлять их для поисковых роботов, чтобы их можно было сканировать. Чтобы сделать это, мы должны сами просканировать все страницы в нашем приложении с помощью автономного браузера и сохранить HTML-файл в файле, который будет обслуживаться. Но из-за необходимости загружать просмотры через ajax и все остальное, мы должны подождать загрузки нашей страницы, прежде чем сохранять HTML-код безголового браузера. В противном случае мы получаем пустой HTML с пустыми представлениями.

Я написал небольшой скрипт, который может проверить готовность наших представлений. Когда свойство $rootScope.status равно 'ready', я знаю, что могу сохранить html-файл моего безголового браузера по завершении загрузки.

var app = angular.module("app", ["ui.router", 'ngAnimate','angular-loading-bar','angular-images-loaded','angular-google-analytics']);

app.run(['$rootScope', function($rootScope){

    $rootScope.loadingCount = 0;
    $rootScope.changeSuccess = false;

    $rootScope.ready = function(){
        $rootScope.loadingCount--;
        if (($rootScope.loadingCount == 0) && ($rootScope.changeSuccess == true)){
            $rootScope.status = 'ready';
        }
    };

    $rootScope.loading = function(){
        $rootScope.loadingCount++;
        $rootScope.status = 'loading';
    };

    $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
        $rootScope.loadingCount = 0;
        $rootScope.changeSuccess = false;
    });

    $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams) {
        $rootScope.changeSuccess = true;
    });

    $rootScope.$on("$viewContentLoading", function(){
        $rootScope.loadingCount++;
    });

    $rootScope.$on("$viewContentLoading", function(){
        $rootScope.loadingCount--;
    });

}]);

Затем в каждом из наших контроллеров мы должны вызвать

$rootScope.loading();

и когда контроллер готов

$rootScope.ready()

Благодаря этому мы сможем проверить, все ли наши контроллеры предоставили свои представления. Пока это не супер-элегантно, но это делает работу.

Этот скрипт может быть хорошо интегрирован с angular-loading-bar, так как он отслеживает готовность всего приложения. Индикатор выполнения может быть индикатором этого прогресса. Недостаток этого заключается в том, что он конфликтует с естественным поведением XHR-запросов отслеживания угловой загрузки.

Например, в моих контроллерах я использую это:

app.controller("WorksController", [

    "$scope", "cfpLoadingBar",

    function ($scope, cfpLoadingBar) {
        cfpLoadingBar.start();
        $scope.imgLoadedEvents = {
            always: function () {
                cfpLoadingBar.complete();
            }
        };
    }
]);

Этот код должен быть перенесен прямо в сценарий $ rootScope, который отслеживает готовность представлений.

$rootScope.$watch('status', function(newValue, oldValue){
    if (newValue == 'loading'){ cfpLoadingBar.start() }
    else if (newValue == 'ready') { cfpLoadingBar.complete() }
})

Тем не менее, angular-progress-bar по-прежнему работает в фоновом режиме. Я активировал XHR-перехватчик. Однако, если XHR-запросы завершены до загрузки изображения, индикатор выполнения исчезает, даже если представления не завершены. Кроме того, если изображение загружено до завершения запроса XHR, индикатор выполнения исчезает.

Как я могу объединить возможности перехвата XHR на угловой загрузочной планке с возможностями перехвата готовности этого вида?

4 ответа

Решение

Это невозможно обойтись без изменения кода angular-loading-bar, потому что перехватчик $http не имеет какого-либо API-интерфейса, к которому вы можете подключиться.

Вы должны раскошелиться на Github и изменить cfb.loadingBarInterceptor, По сути, вам нужно удалить код, который скрывает и показывает полосу загрузки. Оставьте код, который транслирует события на $rootScope, Это то, к чему вы будете цепляться.

  return {
    'request': function(config) {
      // Check to make sure this request hasn't already been cached and that
      // the requester didn't explicitly ask us to ignore this request:
      if (!config.ignoreLoadingBar && !isCached(config)) {
        $rootScope.$broadcast('cfpLoadingBar:loading', {url: config.url});            
      }
      return config;
    },

    'response': function(response) {
      if (!response || !response.config) {
        $log.error('Broken interceptor detected: Config object not supplied in response:\n https://github.com/chieffancypants/angular-loading-bar/pull/50');
        return response;
      }

      if (!response.config.ignoreLoadingBar && !isCached(response.config)) {
        $rootScope.$broadcast('cfpLoadingBar:loaded', {url: response.config.url, result: response});
      }
      return response;
    },

    'responseError': function(rejection) {
      if (!rejection || !rejection.config) {
        $log.error('Broken interceptor detected: Config object not supplied in rejection:\n https://github.com/chieffancypants/angular-loading-bar/pull/50');
        return $q.reject(rejection);
      }

      if (!rejection.config.ignoreLoadingBar && !isCached(rejection.config)) {
        $rootScope.$broadcast('cfpLoadingBar:loaded', {url: rejection.config.url, result: rejection});
      }
      return $q.reject(rejection);
    }
  };

Теперь, в вашем блоке выполнения, просто добавьте эти строки внизу:

app.run(['$rootScope', function($rootScope){
  // Previous code ...

  $rootScope.$on("cfbLoadingBar:loaded", $rootScope.ready);
  $rootScope.$on("cfbLoadingBar:loading", $rootScope.loading);

});

В случае, если неясно, что это делает, используются события от вашего нового перехватчика $http, что приводит к ready а также loading вызывается так же, как ваши контроллеры.

Как указано @anid-monsur, это невозможно сделать без изменения штрих-кода загрузки. Тем не менее, я хотел сохранить первоначальную функциональность angular-loading-bar, поэтому пошел по другому пути, который он предложил.

Вместо того, чтобы удалять код скрытия шоу, я добавил пользовательские обработчики событий, подобные этим, в перехватчике angular-loading-bar:

$rootScope.$on("cfpLoadingBar:customRequest", function(event, args){
    if (!args) {
      args = {};
    }
    if (!args.url){
      args.url = 'custom';
    }
    loading(args);
  });

  $rootScope.$on("cfpLoadingBar:customResponse", function(event, args){
    if (!args) {
      args = {};
    }
    if (!args.config){
      args.config = {};
    }
    if (!args.config.url){
      args.config.url = 'custom';
    }
    response(args);
  }); 

Проверьте код здесь

Итак, когда мой ui-роутер меняет состояние, я могу написать:

app.run(['$rootScope', 'cfpLoadingBar', function($rootScope, cfpLoadingBar){

    $rootScope.$broadcast("cfpLoadingBar:customRequest");

    $rootScope.ready = function(){
        $rootScope.$broadcast("cfpLoadingBar:customResponse");
    };

    $rootScope.loading = function(){
        $rootScope.$broadcast("cfpLoadingBar:customRequest");
    };

    $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
        $rootScope.$broadcast("cfpLoadingBar:customRequest");
    });

    $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams) {
        $rootScope.$broadcast("cfpLoadingBar:customResponse");
    });

}]);

Обратите внимание на добавленные $rootScope.ready() и $rootScope.loading()

Они используются, когда у контроллера есть отложенный процесс загрузки

Например, с angular-imagesloaded:

app.controller("WorksController", [
    "$scope", '$rootScope',

    function ($scope, $rootScope) {

        $rootScope.loading();

        $scope.imgLoadedEvents = {
            done: function () {
                $rootScope.ready();
            }
        };

    }
]);

Таким образом, я все еще могу использовать собственный перехватчик, предоставляемый angular-loading-bar, и регистрировать свои собственные "пользовательские" запросы.

Пост, на который вы ссылаетесь, является наследием.

Google будет индексировать ваше одностраничное приложение без HTML-снимков.


Если вы хотите создавать моментальные снимки (например, для использования канонического тега, который еще не поддерживается!), Вам НЕ следует делать это вручную, а интегрировать задачу в процесс сборки. С Grunt/Gulp это очень просто и может быть сделано за 10 минут. Нет необходимости указывать "готовое" событие - phantomjs распознает, когда ваш скрипт будет выполнен.

Правильным способом было бы переместить функцию, которая загружает ваши изображения в функцию разрешения ваших состояний. Тогда вам не нужно беспокоиться об отправке готовых событий в фантом.;-) Чтобы использовать это с полосой загрузки:

  $rootScope.$on('$stateChangeStart', function () {
            ngProgress.start();

              // or your:  cfpLoadingBar.start();

        });
  $rootScope.$on('$stateChangeSuccess', function () {
        ngProgress.complete();

               // or your:  cfpLoadingBar.complete();

        });

Эта проблема связана с плагином, как сказала Анид Монсур. Вы можете решить проблему, просто обновив angular-loading-bar до последней версии или хотя бы 7.0.1+.

Другие вопросы по тегам