【问题标题】:angular-loading-bar and images/views/states loading角度加载栏和图像/视图/状态加载
【发布时间】:2015-10-03 14:41:46
【问题描述】:

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

当我偶然发现 post 关于如何使用 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 拦截功能与此视图就绪拦截功能集成?

【问题讨论】:

    标签: angularjs angular-ui-router imagesloaded


    【解决方案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 以与控制器相同的方式被调用。

    【讨论】:

    • 感谢您的回答。事实上,我确实 fork 了 repo,但我没有像你那样更改代码,而是使用我在他们的代码中添加的自定义事件处理程序模拟了 http 请求。您可以查看github.com/ludydoo/angular-loading-bar/commit/…。然后,当触发 ui-router onStateChangeStart 或 onStateChangeSuccess 时,我会触发那些模拟请求。
    【解决方案2】:

    您所指的博文是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();
    
            });
    

    【讨论】:

    • 嗨。首先,感谢您的回答。但是我认为您错过了问题的重点(检查标题),即自然拦截 XHR 请求的 angular-loading-bar 与手动 cfpLoadingBar.start() 和 cfpLoadingBar.complete() 之间的冲突。我在关于 Angular SEO 的帖子中发现有用的是它在状态准备就绪时检测到的部分。这就是我用来挂钩 angular-loading-bar 的代码。
    • 但感谢您指定 Google 可以索引 SPA。我仍然发现为禁用 javascript 的客户端创建快照很有用,使用
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        猜你喜欢
        • 2018-05-28
        • 2019-11-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多