【问题标题】:(Angular-ui-router) Show loading animation during resolve process(Angular-ui-router) 在解析过程中显示加载动画
【发布时间】:2013-09-28 11:20:01
【问题描述】:

这是一个两部分的问题:

  1. 我正在使用 $stateProvider.state() 中的 resolve 属性在加载控制器之前获取某些服务器数据。在此过程中如何显示加载动画?

  2. 我的子状态也使用了 resolve 属性。问题是 ui-router 似乎想在加载 any 控制器之前完成 all 解析。有什么办法可以让父控制器在解决它们的解析后加载,而不必等待所有子控制器解析?这个问题的答案很可能也解决了第一个问题。

【问题讨论】:

  • 你解决过这个问题吗?
  • @StefanHenze 不,我只是认为这是一个 ui-router 架构缺陷。
  • 这里有一个关于这个确切问题的讨论:github.com/angular-ui/ui-router/issues/456
  • @StefanHenze 哈哈,没有双关语......

标签: javascript angularjs angular-ui-router


【解决方案1】:

我使用仅当$http 有待处理请求时才显示的动画 gif。

在我的基本页面模板中,我有一个导航栏和导航栏控制器。控制器的相关部分如下所示:

controllers.controller('NavbarCtrl', ['$scope', '$http',
    function ($scope, $http) {
        $scope.hasPendingRequests = function () {
            return $http.pendingRequests.length > 0;
        };
    }]);

我的html中对应的代码是:

<span class="navbar-spinner" ng-show="hasPendingRequests()">
    <img src="/static/img/spinner.gif">
</span>

希望对你有帮助!

【讨论】:

  • 正如您从我的问题中看到的那样,在所有解决方案都完成之前,我无法访问控制器(这是问题所在)!
  • 你不应该对 ng-show 或 ng-if 使用函数,因为它们会在每个摘要循环中被调用。这会降低性能。
【解决方案2】:

属性解析后,如何将内容添加到 ui-router 填充的 div 中?

在你的index.html

<div ui-view class="container">
    Loading....
</div>

用户现在将在解析属性时看到“正在加载...”。一切准备就绪后,内容将被 ui-router 替换为您的应用内容。

【讨论】:

  • 如果您的应用程序可以有完整的应用程序加载替换,这将起作用,但如果您想在布局中分层构建“正在加载...”(例如,显示应用程序的一部分,另一部分显示正在加载...)。
  • 这仍然是一种快速/肮脏/简单的方法,可以让您继续处理更重要的事情……例如加快解决问题。
  • 如果您将多个模板加载到该视图中,理想情况下您会希望这样做。 “正在加载....”消息只会在第一次出现。
  • 这在 Ionic 中可行吗?我无法在 Ionic 中使用这种方式。
【解决方案3】:

编辑:这是一个更简单的解决方案,经过测试并且运行良好:

在我的主控制器中,我只有

$scope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
    if (toState.resolve) {
        $scope.showSpinner();
    }
});
$scope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams) {
    if (toState.resolve) {
        $scope.hideSpinner();
    }
});

当我们即将进入一个有任何问题要解决的状态时,它会显示微调器,并在状态更改完成时隐藏它。您可能想要添加一些检查状态层次结构(即,如果正在加载的父状态解决了某些问题,也显示微调器)但是这个解决方案对我来说很好。

这是我的旧建议作为参考和替代方案:

  1. 在您的应用程序控制器中,监听stateChangeStart 事件并检查您是否即将切换到您想要在解析期间显示微调器的状态(请参阅https://github.com/angular-ui/ui-router/wiki/Quick-Reference#wiki-events-1

    $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams){
        if (toState.name == 'state.with.resolve') {
            $scope.showSpinner();  //this is a function you created to show the loading animation
        }
    })
    
  2. 当你的控制器最终被调用时,你可以隐藏微调器

    .controller('StateWithResolveCtrl', function($scope) {
        $scope.hideSpinner();
    })
    

您还可能希望通过侦听$stateChangeError 事件并在处理错误时隐藏动画来检查解析期间可能发生的任何错误。

当您在控制器之间分配微调器的逻辑时,这并不完全干净,但这是一种方式。希望对您有所帮助。

【讨论】:

  • if (toState.resolve) 检查状态配置是否有解析字典。就我而言,这是对状态进行异步服务调用的足够好的估计。我假设如果有一个解析块,它是得到解决的服务调用。仅当进行服务调用时,代码才会显示和隐藏微调器。如上所述,此代码可能需要改进以检查父状态,具体取决于您的用例。
  • 在“旧建议”中,您在调用 $rootScope 时引用了 $scope$scope 是从哪里来的?
  • @pdeva,事件处理程序是在某个控制器内部定义的;这也是定义showSpinner 函数的地方。
  • 我无法使用toState.resolve,所以我使用了fromState.name !== toState.name。除此之外,您的回答很棒,做得很好!
【解决方案4】:

我开发了以下解决方案,非常适合我。

1.添加以下app.run

app.run(function($rootScope){

    $rootScope
        .$on('$stateChangeStart', 
            function(event, toState, toParams, fromState, fromParams){ 
                $("#ui-view").html("");
                $(".page-loading").removeClass("hidden");
        });

    $rootScope
        .$on('$stateChangeSuccess',
            function(event, toState, toParams, fromState, fromParams){ 
                $(".page-loading").addClass("hidden");
        });

});

2。将加载指示器放在 ui-view 的正上方。将 id="ui-view" 添加到 ui-view div。

<div class="page-loading">Loading...</div>
<div ui-view id="ui-view"></div>

3.将以下内容添加到您的 CSS 中

.hidden {
  display: none !important;
  visibility: hidden !important;
}

注意:

A.上面的代码会在两种情况下显示加载指示器1) Angular 应用第一次加载时2) 视图改变时。

B.如果您不希望在第一次加载 Angular 应用程序时(在加载任何视图之前)显示指示器,则将 hidden 类添加到加载 div 中,如下所示

<div class="page-loading hidden">Loading...</div>

【讨论】:

  • run 代码不是 Angular 方式。与其在那里操作 DOM,不如设置范围变量,如 loadingVisibleviewVisible,然后在 HTML 中,强制说明元素何时可见或隐藏,例如:&lt;div class="page-loading" ng-show="viewVisible"&gt;Loading...&lt;/div&gt;。不过,为了清空视图,它可能需要一个自定义指令。
  • 不建议这样做。在 angularcontext 中使用 jQuery 很讨厌,并且会在视图和控制器之间创建不需要的依赖关系——这不是一个好习惯!
  • 我喜欢这个,但是......它真的与 DOM 相关吗?
  • 像@MatthiasDailey 建议的这种“角度方式”的问题是您需要在整个地方复制显示/隐藏逻辑。这个解决方案的巧妙之处在于您可以使用全局方法来显示/隐藏内容或加载器。如果您真的想以 Angular 方式执行此操作,请创建一个包装 ui-view 的新指令
  • 我认为 ng-class 会是一个更好的方法。
【解决方案5】:

我发现由于网络访问,使用 angular-loading-bar 非常适合长时间解析。

【讨论】:

    【解决方案6】:

    我的想法是在$stateChangeStart 上的转换状态之间走状态图的路径,并收集所有涉及的视图。然后每个ui-view 指令都会监视相应的视图是否涉及转换,并在其元素上添加'ui-resolving' 类。

    plunker 演示引入了两个根状态:firstsecond,后者有两个子状态second.sub1second.sub2。状态 second.sub2 还针对属于其祖父母的 footer 视图。

    【讨论】:

      【解决方案7】:

      我更喜欢使用指令来匹配任何加载活动,主要是为了保持我的代码库干净

      angular.module('$utilityElements', [])
      .directive('loader',['$timeout','$rootScope', function($timeout, $rootScope) {
          return {
            restrict: 'A',
            template: '<div id="oneloader" class="hiddenload">loading...</div>',
            replace: true,
            compile: function (scope, element, attrs) {
              $timeout(function(){
                $rootScope
                    .$on('$stateChangeStart',
                        function(event, toState, toParams, fromState, fromParams){
                            $("#oneloader").removeClass("hiddenload");
                    });
      
                $rootScope
                    .$on('$stateChangeSuccess',
                        function(event, toState, toParams, fromState, fromParams){
                            //add a little delay
                            $timeout(function(){
                              $("#oneloader").addClass("hiddenload");
                            },500)
                    });
              }, 0);
            }
          }
        }]);
      

      【讨论】:

        【解决方案8】:

        如果有人在使用ngRoute,在加载下一个视图之前等待resolve,并为ui使用angular-bootstrap-ui,您可以使用the following

        app.config([
          "$routeProvider", "$locationProvider", function($routeProvider, $locationProvider) {
            return $routeProvider.when("/seasons/:seasonId", {
              templateUrl: "season-manage.html",
              controller: "SeasonManageController",
              resolve: {
                season: [
                  "$route", "$q", "$http", "$modal", function($route, $q, $http, $modal) {
                    var modal, promise, seasonId;
                    modal = $modal.open({
                      backdrop: "static",
                      template: "<div>\n  <div class=\"modal-header\">\n    <h3 class=\"modal-title\">\n      Loading...\n    </h3>\n  </div>\n  <div class=\"modal-body\">\n    <progressbar\n      class=\"progress-striped active\"\n      value=\"'100'\">\n    </progressbar>\n  </div>\n</div>",
                      keyboard: false,
                      size: "lg"
                    });
                    promise = $q.defer();
                    seasonId = $route.current.params.seasonId;
                    $http.get("/api/match/seasons/" + seasonId).success(function(data) {
                      modal.close();
                      promise.resolve(data);
                    }).error(function(data) {
                      modal.close();
                      promise.reject(data);
                    });
        
                    return promise.promise;
                  }
                ]
              }
            });
          }
        ]);
        

        【讨论】:

          【解决方案9】:

          我在路由器中使用 resole 时使用视图的想法非常棒。尝试这个。

          //edit index.html file 
          <ion-nav-view>
              <div ng-show="loadder" class="loddingSvg">
                  <div class="svgImage"></div>
              </div>
          </ion-nav-view>
          
          // css file
          
          .loddingSvg {
              height: 100%;
              background-color: rgba(0, 0, 0, 0.1);
              position: absolute;
              z-index: 99;
              left: 0;
              right: 0;
          }
          
          .svgImage {
              background: url(../img/default.svg) no-repeat;
              position: relative;
              z-index: 99;
              height: 65px;
              width: 65px;
              background-size: 56px;
              top: 50%;
              margin: 0 auto;
          }
          
          // edit app.js
          
           .run(function($ionicPush, $rootScope, $ionicPlatform) {
          
          
          
          
                  $rootScope.$on('$stateChangeStart',
                      function(event, toState, toParams, fromState, fromParams) {
                          $rootScope.loadder = true;
                      });
          
                  $rootScope.$on('$stateChangeSuccess',
                      function(event, toState, toParams, fromState, fromParams) {
                          $rootScope.loadder = false;
                      });
          
          });
          

          【讨论】:

            【解决方案10】:

            这是页面在任何状态(任何页面)之间导航时的全局加载器,放入 app.js

            .run(
                ['$rootScope',
                    function($rootScope) {
                        $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
                            $rootScope.preloader = true;
                        })
                        $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams) {
                            $rootScope.preloader = false;
                        })
                    }
                ])
            

            在html中:

            <div ng-show="preloader">Loading...</div>
            

            【讨论】:

            • 完美!谢谢!
            • 再明显不过了......也许可以解释一下在加载过程中如何将某些内容放入 ui-view 中?定义 2 个变量是如此简单
            【解决方案11】:

            $stateChangeStart 等的使用已被弃用,取而代之的是Transition Hooks。所以,对于 Stefan Henze 的回答,更新后的版本是:

            $transitions.onStart({}, function(transition) {
              if (transition.to().resolve) {
                $scope.showSpinner();
              }
            });
            
            $transitions.onSuccess({}, function(transition) {
              if (transition.to().resolve) {
                $scope.hideSpinner();
              }
            });
            

            你可以在你的父控制器中使用它。记得注入$transitions -

            .controller('parentController',['$transitions',function($transitions){...}]);
            

            另外,请记住,作为空对象的 resolve 仍将呈现 transition.to().resolve == true,因此不要在状态声明中留下空的占位符 resolve

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-09-11
              • 2015-07-05
              • 1970-01-01
              • 2013-08-02
              相关资源
              最近更新 更多