Угловая загрузка бар и загрузка изображений / представлений / состояний
Я использую 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+.