web-dev-qa-db-ja.com

UIルーター解決中にロードスピナーを適用する

$routeProviderresolveプロパティにより、対応するビューがレンダリングされる前にいくつかのジョブを実行できます。

ユーザーエクスペリエンスを向上させるために、これらのジョブの実行中にスピナーを表示したい場合はどうすればよいですか?
確かに、そうでなければ、たとえば600ミリ秒以上のミリ秒の間にビュー要素が表示されなかったため、ユーザーはアプリケーションがブロックされたと感じるでしょう。

もちろん、$scope.$rootChangeStart関数のおかげでスピナーを表示するために、グローバルなdiv要素を定義する方法がありました現在のビューの外
ただし、ページ全体を非表示にしたいのですが、中央にスピナーが表示されているだけです。
ロードの表示方法に関して、webappのいくつかのページが異なるようにします。

この興味深い投稿 上記の正確な問題が含まれています。

このアプローチは、ひどいUIエクスペリエンスをもたらします。ユーザーがボタンをクリックしてリストまたは何かを更新すると、ライブラリには実際に状態の変化の影響を受けるビューに対してのみスピナーを表示する方法がないため、一般的なスピナーで画面全体が覆われます。結構です。

いずれにせよ、この問題を提出した後、「解決」機能はアンチパターンであることに気付きました。すべての約束が解決するのを待ってから、状態の変化をアニメーション化します。これは完全に間違っています。ステート間の遷移アニメーションをデータロードと並行して実行し、前者で後者をカバーできるようにします。

たとえば、アイテムのリストがあり、そのうちの1つをクリックすると、リストが非表示になり、アイテムの詳細が別のビューに表示されるとします。アイテムの詳細に平均400ミリ秒かかる非同期ロードがある場合、ほとんどの場合、リストビューで300ミリ秒の「離脱」アニメーションと300ミリ秒の「入力」アニメーションを使用することで、負荷をほぼ完全にカバーできます。アイテム詳細ビューで。このようにして、UIに滑らかな感触を与え、ほとんどの場合、スピナーをまったく表示しないようにします。

ただし、これには非同期ロードと状態変更アニメーションを同時に開始する必要があります。 「解決」を使用すると、アニメーションの開始前に非同期アニメーション全体が発生します。ユーザーがクリックすると、スピナーが表示され、遷移アニメーションが表示されます。状態の変更には全体で約1000ミリ秒かかりますが、遅すぎます。

「Resolve」は、Promiseを待たないオプションがある場合に異なるビュー間の依存関係をキャッシュする便利な方法ですが、状態変更が開始される前に常にそれらを解決する現在の動作、IMOはほとんど役に立たなくなります。非同期ロードを伴う依存関係については避ける必要があります。

本当にresolveを使用して一部のデータをロードするのをやめて、対応するコントローラーに直接ロードし始める必要がありますか?そのため、ジョブが実行されている限り、グローバルではなく、ビュー内の目的の場所で対応するビューを更新できます。

35
Mik378

$routeChangeStartをリッスンするディレクティブを使用できます。たとえば、起動時に要素を表示します。

app.directive('showDuringResolve', function($rootScope) {

  return {
    link: function(scope, element) {

      element.addClass('ng-hide');

      var unregister = $rootScope.$on('$routeChangeStart', function() {
        element.removeClass('ng-hide');
      });

      scope.$on('$destroy', unregister);
    }
  };
});

次に、特定のビューのローダーに配置します。次に例を示します。

表示1:

<div show-during-resolve class="alert alert-info">
  <strong>Loading.</strong>
  Please hold.
</div>

表示2:

<span show-during-resolve class="glyphicon glyphicon-refresh"></span>

このソリューション(および他の多くのソリューション)の問題は、外部サイトからいずれかのルートを参照すると、以前のng-viewテンプレートがロードされないため、解決中にページが空白になる可能性があることです。

これは、フォールバックローダーとして機能するディレクティブを作成することで解決できます。 $routeChangeStartをリッスンし、前のルートがない場合にのみローダーを表示します。

基本的な例:

app.directive('resolveLoader', function($rootScope, $timeout) {

  return {
    restrict: 'E',
    replace: true,
    template: '<div class="alert alert-success ng-hide"><strong>Welcome!</strong> Content is loading, please hold.</div>',
    link: function(scope, element) {

      $rootScope.$on('$routeChangeStart', function(event, currentRoute, previousRoute) {
        if (previousRoute) return;

        $timeout(function() {
          element.removeClass('ng-hide');
        });
      });

      $rootScope.$on('$routeChangeSuccess', function() {
        element.addClass('ng-hide');
      });
    }
  };
});

フォールバックローダーは、ng-viewを使用して要素の外側に配置されます。

<body>
  <resolve-loader></resolve-loader>
  <div ng-view class="fadein"></div>
</body>

すべてのデモ:http://plnkr.co/edit/7clxvUtuDBKfNmUJdbL3?p=preview

51
tasseKATT

これはかなりきれいだと思う

app.run(['$rootScope', '$state',function($rootScope, $state){

  $rootScope.$on('$stateChangeStart',function(){
      $rootScope.stateIsLoading = true;
 });


  $rootScope.$on('$stateChangeSuccess',function(){
      $rootScope.stateIsLoading = false;
 });

}]);

そして、ビューで

<div ng-show='stateIsLoading'>
  <strong>Loading.</strong>
</div>
29
Pranay Dutta

プラナイの答えをさらに進めるために、これが私がやった方法です。

JS:

app.run(['$rootScope',function($rootScope){

    $rootScope.stateIsLoading = false;
    $rootScope.$on('$routeChangeStart', function() {
        $rootScope.stateIsLoading = true;
    });
    $rootScope.$on('$routeChangeSuccess', function() {
        $rootScope.stateIsLoading = false;
    });
    $rootScope.$on('$routeChangeError', function() {
        //catch error
    });

}]);

HTML

<section ng-if="!stateIsLoading" ng-view></section>
<section ng-if="stateIsLoading">Loading...</section>
11
sidonaldson

私はこれに2年遅れており、はい、これらの他のソリューションは動作しますが、このようなすべての実行ブロックでこれらすべてを処理する方が簡単であることがわかります

.run(['$rootScope','$ionicLoading', function ($rootScope,$ionicLoading){


  $rootScope.$on('loading:show', function () {
    $ionicLoading.show({
        template:'Please wait..'
    })
  });

  $rootScope.$on('loading:hide', function () {
    $ionicLoading.hide();
  });

  $rootScope.$on('$stateChangeStart', function () {
    console.log('please wait...');
    $rootScope.$broadcast('loading:show');
  });

  $rootScope.$on('$stateChangeSuccess', function () {
    console.log('done');
    $rootScope.$broadcast('loading:hide');
  });

}])

他に何も必要ありません。とても簡単です。動作中の です。

3
garrettmac

Ui-router 1.0では、$ stateChange *イベントは非推奨です。代わりに遷移フックを使用してください。詳細については、下記の移行ガイドをご覧ください。

https://ui-router.github.io/guide/ng1/migrate-to-1_0#state-change-events

1
Nicholas Glazer

「$ stateChangeStart」および「$ stateChangeSuccess」の問題は、「$ rootScope.stateIsLoading」が最後の状態に戻ったときに更新されないことです。その解決策はありますか?私も使用しました:

    $rootScope.$on('$viewContentLoading',
        function(event){....});

そして

    $rootScope.$on('$viewContentLoaded',
        function(event){....});

しかし、同じ問題があります。

0
Hamid Hoseini