(Angular-ui-router)解決プロセス中に読み込みアニメーションを表示する


80

これは2つの部分からなる質問です。

  1. $ stateProvider.state()内のresolveプロパティを使用して、コントローラーをロードする前に特定のサーバーデータを取得しています。このプロセス中にロードアニメーションを表示するにはどうすればよいですか?

  2. resolveプロパティも利用する子状態があります。問題は、ui-routerがコントローラーをロードする前にすべての解決策を確定したいように見えることです。子のすべての解決を待たずに、解決が解決されたら親コントローラーをロードする方法はありますか?これに対する答えは、おそらく最初の問題も解決するでしょう。


4
この問題を解決したことはありますか?
Stefan Henze

3
@StefanHenzeいいえ、これをui-routerのアーキテクチャ上の欠陥として受け入れました。
マットウェイ

2
この正確な問題についての議論はここにあります:github.com/angular-ui/ui-router/issues/456
Johnny

6
@StefanHenze lol、しゃれは意図されていません....
Dave

回答:


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)状態構成に解決ディクショナリがあるかどうかを確認します。私の場合、これは州が非同期サービス呼び出しを行うのに十分な見積もりです。解決ブロックがある場合、解決されるのはサービス呼び出しだと思います。このコードは、サービス呼び出しが行われた場合にのみスピナーを表示および非表示にします。上で説明したように、ユースケースによっては、親の状態もチェックするようにこのコードを改良する必要がある場合があります。
シュテファン・ヘンツェ2014年

「古い提案」では$scope、の呼び出し内で参照し$rootScopeます。ここ$scopeからどこから来るの?
pdeva 2014年

@pdeva、イベントハンドラーはいくつかのコントローラー内で定義されました。ここでshowSpinner関数も定義されました。
Stefan Henze 2014年

使えなかったので使っtoState.resolveてみましたfromState.name !== toState.name。それを除けば、あなたの答えは素晴らしく、よくできています!
JacobHulse19年

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-viewdivに追加します。

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

3.以下をCSSに追加します

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

注意:

A.上記のコードは、2つの場合にロードインジケーターを表示します。1) Angularアプリが最初にロードされたとき、2)ビューが変更されたとき。

B.角度のあるアプリが最初に読み込まれるとき(ビューが読み込まれる前)にインジケーターを表示したくない場合は、以下のように読み込みdivに非表示のクラスを追加します

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

15
そのrunコードは、AngularWayではありません。代わりに、そこにDOMを操作する、などのセットスコープ変数loadingVisibleviewVisibleし、要素が見えるかのように、隠されているときにHTMLに、命令的に言います:<div class="page-loading" ng-show="viewVisible">Loading...</div>。ただし、ビューを空にするには、カスタムディレクティブが必要になる場合があります。
マティアス2015年

2
これはお勧めしません。anglecontext内でjQueryを使用することは厄介であり、ビューとコントローラーの間に望ましくない依存関係を作成しています-良い習慣ではありません!
Silas Hansen

私はこれが好きですが...それはDOMに関して本当に正しいですか?
contributorpw

@MatthiasDaileyのように、これを「角度のある方法」で行う場合の問題は、表示/非表示ロジックをあちこちに複製する必要があることです。このソリューションの優れた点は、コンテンツまたはローダーを表示/非表示にするグローバルメソッドを使用できることです。本当にこれをAngularの方法で実行したい場合は、ui-viewをラップする新しいディレクティブを作成します
thomaux 2016年

1
これにはng-classの方が良いアプローチだと思います。
userX 2016


8

プロパティが解決されたらui-routerによって入力されるコンテンツをdivに追加するのはどうですか?

あなたの中で index.html

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

プロパティが解決されている間、ユーザーには「読み込み中...」と表示されます。すべての準備が整うと、コンテンツはui-routerによってアプリのコンテンツに置き換えられます。


7
これは、アプリケーションでアプリの完全な読み込みを置き換えることができる場合は機能しますが、レイアウトに「読み込み中...」を階層的に構築する場合は不十分です(たとえば、アプリケーションの一部を表示し、別の部分で読み込みを表示する... )。
マットウェイ

1
それでも、解決をスピードアップするなど、より重要なことに進むための迅速/汚い/簡単な方法です。
Brian Vanderbusch 2014年

3
そのビューに複数のテンプレートをロードする場合、理想的にはそれを実行します。「loading ....」メッセージは初めて表示されます。
Yasser Shaikh 2015年

これはIonicで可能ですか?Ionicではこの方法を使用できませんでした。
anoop.PA 2016年

7

$http保留中のリクエストがある場合にのみ表示するアニメーションGIFを使用しています。

ベースページテンプレートには、navbarとnavbarコントローラーがあります。コントローラの関連部分は次のようになります。

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
私の質問からわかるように、すべての解決策が完了するまで、コントローラーにアクセスできません(これが問題です)。
マットウェイ

1
ng-showまたはng-ifは各ダイジェストループで呼び出されるため、これらの関数は使用しないでください。パフォーマンスが低下します。
Vikas Gautam 2017

4

私の考えは、遷移状態間の状態グラフ上のパスを歩き、$stateChangeStart関連するすべてのビューを収集することです。次に、すべてのui-viewディレクティブは、対応するビューが遷移に関与しているかどうかを監視し、その'ui-resolving'要素にクラスを追加します。

plunkerの:デモは2つのルートの状態を紹介firstし、second後者は2つのサブ状態を持ち、second.sub1そしてsecond.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 2017

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

ルーターで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;
            });

});

0

誰かがを使用していてngRouteresolve次のビューをロードする前に待機し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;
          }
        ]
      }
    });
  }
]);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.