(Angular-ui-router)在解析过程中显示加载动画


80

这是一个两部分的问题:

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

  2. 我有一些子状态也利用了resolve属性。问题是ui-router似乎要在加载任何控制器之前完成所有解析。有什么方法可以让父控制器在解析解决后加载,而不必等待所有子解析都加载?对此的答案可能也将解决第一个问题。


4
您是否解决了这个问题?
Stefan Henze 2013年

3
@StefanHenze不,我只是接受这作为ui路由器的体系结构缺陷。
Matt Way

2
这里有关于这个确切问题的讨论:github.com/angular-ui/ui-router/issues/456
Johnny Oshika

6
@StefanHenze大声笑,没有双关语的意图。...–
Dave

Answers:


71

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

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

$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在处理错误时侦听事件并隐藏动画来检查在解析过程中可能发生的任何错误。

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


1
if (toState.resolve)检查状态配置是否具有解析字典。就我而言,这足以估计该状态是否进行了异步服务调用。我假设如果有一个resolve块,则是服务调用得到解决。该代码仅在进行服务调用时显示和隐藏微调框。如上所述,根据您的用例,可能需要改进此代码以检查父状态。
Stefan Henze 2014年

在“旧建议”中,您$scope在上引用了$rootScope。哪里的$scope从这里来?
pdeva 2014年

@pdeva,事件处理程序是在某个控制器中定义的;那也是showSpinner定义函数的地方。
Stefan Henze 2014年

我不能用toState.resolve所以我用了fromState.name !== toState.name。除此之外,您的答案很棒,做得很好!
Jacob Hulse

22

我开发了以下最适合我的解决方案。

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视图上方。将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)首次加载有角度的应用程序时2)更改视图时。

B.如果您不希望在有角度的应用程序首次加载时(在加载任何视图之前)显示该指示器,则将隐藏类添加到加载div中,如下所示

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

15
run代码不是Angular Way。不用在那儿操纵DOM,而是设置范围变量,例如loadingVisibleviewVisible,然后在HTML中,强制性地说出元素何时可见或隐藏,例如:<div class="page-loading" ng-show="viewVisible">Loading...</div>。为了清空视图,它可能需要一个自定义指令。
马提亚斯(Matthias)2015年

2
不建议这样做。在angularcontext中使用jQuery很讨厌,并且在视图和控制器之间造成了不必要的依赖-不好的习惯!
西拉斯·汉森

我喜欢这个,但是...关于DOM真的正确吗?
contributorpw

像@MatthiasDailey这样的“成角度的方式”提出的问题是,您需要在各处重复显示/隐藏逻辑。这个解决方案的好处是,您可以使用全局方法来显示/隐藏内容或加载程序。如果您真的想以Angular的方式执行此操作,则创建一个包含ui视图的新指令
thomaux

1
我认为ng-class将是更好的方法。
UserX 2016年


8

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

在你的 index.html

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

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


7
如果您的应用程序可以替换完整的应用程序,这将起作用,但是如果您要在布局中分层构建“ loading ...”(例如,显示应用程序的一部分,而另一部分显示加载的内容... )。
Matt Way

1
仍然是一种快速/肮脏/轻松的方法,可让您继续进行更重要的事情,例如加快解决方案的速度。
Brian Vanderbusch 2014年

3
如果要将多个模板加载到该视图中,则理想情况下是希望这样做。“正在加载...”消息仅在第一次出现。
Yasser Shaikh 2015年

在Ionic中这可能吗?我无法在Ionic中使用这种方式。
Anoop.PA,2016年

7

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

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

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>

希望对您有所帮助!


3
正如您所看到的,在所有解决方案都完成之前,我无权访问控制器(这就是问题所在)!
马特威(Matt Way),

1
不应将函数用于ng-show或ng-if,因为它们将在每个摘要循环中调用。它将降低性能。
Vikas Gautam

4

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

插件示例演示了两个根状态:firstsecond,后者具有两个子状态second.sub1second.sub2。该州second.sub2还针对footer属于其祖父母的视图。


3

当页面在任何状态(任何页面)之间导航时,这是全局加载程序,放在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视图中?定义2个变量非常简单
DDD

2

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

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);
      }
    }
  }]);

2

采用$stateChangeStart等所具有的,因为被弃用,取而代之的 过渡钩。因此,对于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仍为空对象的a仍将呈现transition.to().resolve == true,因此不要resolve在状态声明中保留空的占位符。


1

我的想法是在路由器中使用鞋垫时使用视图,效果非常好。试试这个。

//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;
            });

});

0

如果有人在使用ngRoute,等待resolve下一次加载下一个视图并angular-bootstrap-ui用于ui,则可以执行以下操作

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;
          }
        ]
      }
    });
  }
]);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.