angular-loading-bar 和 images/views/states loading

angular-loading-bar and images/views/states loading

我正在为我的 angular 应用程序使用 ui-router。我有一些包含图像的视图。我希望 angular-loading-bar 也能跟踪这些图像何时完成加载。或者更一般地说,直到视图完成渲染。

当我偶然发现 post 关于如何使用 angularJS 进行 SEO 时,它表明我们必须为每个 "page" 创建静态 html 快照并提供服务将它们发送给搜索引擎机器人,以便它们可以被抓取。为此,我们必须自己使用无头浏览器抓取应用程序中的所有页面,并将 html 存储到将提供的文件中。但是,由于 angular 必须通过 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 很好地集成,因为它跟踪整个应用程序的准备情况。进度条可以指示该进度。这样做的缺点是它与 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 请求完成之前加载了图像,则进度条会消失。

如何将 angular-loading-bar 的 XHR 拦截功能与此视图就绪拦截功能集成?

如果不修改 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 拦截器的事件,这会导致 readyloading 以与您的控制器相同的方式被调用。

您所指的博文是legacy

Google 将索引 您的单页应用程序 没有 任何 html 快照。


如果您想创建快照(例如,使用尚不支持的规范标签!)您不应手动执行此操作,而应将任务集成到您的构建过程中。使用 Grunt/Gulp 这非常简单,可以在 10 分钟内完成。无需指定 "ready" 事件 - phantomjs 将识别您的脚本何时完成。

正确的方法是将加载图像的函数移动到状态解析函数中。然后你就不用担心将准备好的事件发送给 phantom 了。 ;-) 要将其与加载栏一起使用:

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

              // or your:  cfpLoadingBar.start();

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

               // or your:  cfpLoadingBar.complete();

        });

如@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);
  }); 

查看代码here

所以,当我的 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 提供的本地拦截器并注册我自己的 "custom" 请求。

正如 Anid Monsur 所说,此问题与插件有关。您只需将 angular-loading-bar 升级到 最新版本 或至少 7.0.1+ 即可解决问题。