1

我正在为我的角度应用程序使用 ui-router。我有一些包含图像的视图。我希望角度加载栏也可以在这些图像完成加载时进行跟踪。或者更一般地说,直到视图完成渲染。

当我偶然发现那篇关于如何使用 angularJS 进行 SEO 的帖子时,它表明我们必须为每个“页面”创建静态 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 拦截功能与此视图准备拦截功能集成?

4

4 回答 4

1

如果不修改 angular-loading-bar 的代码,这是不可能做到的,因为 $http 拦截器没有任何可以挂钩的 API。

你应该在 Github 上 fork 项目并修改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以与控制器相同的方式被调用。

于 2015-10-03T16:59:56.817 回答
1

您所指的博文是legacy

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();

        });
于 2015-10-04T10:05:57.300 回答
1

正如@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-router 改变状态时,我可以写:

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 提供的本机拦截器并注册我自己的“自定义”请求。

于 2015-10-04T20:53:42.827 回答
0

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

于 2016-07-19T10:11:01.193 回答