(Angular-ui-router) 해결 프로세스 중 로딩 애니메이션 표시


80

이것은 두 부분으로 구성된 질문입니다.

  1. 컨트롤러를로드하기 전에 특정 서버 데이터를 가져 오기 위해 $ stateProvider.state () 내부의 resolve 속성을 사용하고 있습니다. 이 과정에서 로딩 애니메이션을 표시하려면 어떻게해야합니까?

  2. resolve 속성도 사용하는 자식 상태가 있습니다. 문제는 UI 라우터는 확정 할 것이다 모든 로드하기 전에 결의를 어떤 컨트롤러를. 모든 자식이 해결 될 때까지 기다릴 필요없이 해결이 해결되면 부모 컨트롤러를로드 할 수있는 방법이 있습니까? 이에 대한 대답은 첫 번째 문제도 해결할 수 있습니다.


4
이 문제를 해결 했습니까?
Stefan Henze

3
@StefanHenze 아니요, 방금 이것을 ui 라우터 아키텍처 결함으로 받아 들였습니다.
Matt Way

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 참조). -참조 # 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)상태 구성에 해결 사전이 있는지 확인합니다. 제 경우에는 상태가 비동기 서비스 호출을 수행하는 것으로 충분한 추정치입니다. 해결 블록이 있으면 해결되는 서비스 호출이라고 가정합니다. 이 코드는 서비스 호출이 이루어진 경우에만 스피너를 표시하고 숨 깁니다. 위에서 설명한 것처럼이 코드는 사용 사례에 따라 상위 상태를 확인하기 위해 수정해야 할 수도 있습니다.
Stefan Henze 2014 년

'오래된 제안' $scope에서 $rootScope. 여기서 어디에서 $scope왔습니까?
pdeva 2014-06-04

@pdeva, 이벤트 핸들러가 일부 컨트롤러 내부에 정의되었습니다. showSpinner함수가 정의 된 곳이기도합니다 .
Stefan Henze

내가 사용할 수없는 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 뷰 바로 위에 놓습니다. ui-view div에 id = "ui-view"를 추가합니다.

<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을 조작하는 대신 및와 같은 범위 변수를 설정 한 다음 HTML에서 요소가 표시되거나 숨겨지는시기를 명령 적으로 다음 loadingVisible과 같이 설정 합니다. 보기를 비우려면 사용자 지정 지시문이 필요할 수 있습니다. viewVisible<div class="page-loading" ng-show="viewVisible">Loading...</div>
Matthias

2
이것은 권장되지 않습니다. angularcontext 내에서 jQuery를 사용하는 것은 불쾌하고 뷰와 컨트롤러 사이에 원치 않는 종속성을 생성합니다.
Silas Hansen

나는 이것을 좋아하지만 ... DOM과 관련하여 정말 맞습니까?
contributorpw

@MatthiasDailey가 제안한 것처럼이 '각도'를 수행 할 때 발생하는 문제는 모든 곳에서 표시 / 숨기기 논리를 복제해야한다는 것입니다. 이 솔루션의 깔끔한 점은 콘텐츠 또는 로더를 표시 / 숨기기위한 전역 메서드를 가질 수 있다는 것입니다. 당신이 정말이 각 방법을 수행 할 것입니다 경우, UI를보기 랩하는 새로운 지침 만들기
thomaux

1
나는 ng-class가 이것에 대한 더 나은 접근 방식이라고 생각합니다.
UserX


8

속성이 해결되면 ui-router로 채워질 div에 콘텐츠를 추가하는 것은 어떻습니까?

당신의 index.html

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

이제 속성이 해결되는 동안 사용자에게 "로드 중 ..."이 표시됩니다. 모든 것이 준비되면 콘텐츠는 ui-router로 앱 콘텐츠로 대체됩니다.


7
이는 애플리케이션이 전체 앱 로딩 대체물을 가질 수있는 경우 작동하지만 '로드 중 ...'을 레이아웃에 계층 적으로 빌드하려는 경우에는 부족합니다 (예 : 애플리케이션의 일부를 표시하고 다른 부분은 로딩을 표시하는 중 ... ).
Matt Way

1
결심 속도를 높이는 것과 같은 더 중요한 일로 이동하는 데는 여전히 빠르고 / 더럽고 / 쉬운 방법입니다.
Brian Vanderbusch

3
해당 뷰에 여러 템플릿을로드하는 경우 이상적으로는 원하는 것이 좋습니다. "로드 중 ...."메시지는 처음에만 나타납니다.
Yasser Shaikh 2015 년

Ionic에서 가능합니까? Ionic에서는 이런 식으로 사용할 수 없었습니다.
Anoop.PA

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
내 질문에서 알 수 있듯이 모든 해결이 완료 될 때까지 컨트롤러에 액세스 할 수 없습니다 (이게 문제입니다)!
Matt Way

1
각 다이제스트 루프에서 호출되므로 ng-show 또는 ng-if에 대해 함수를 사용하면 안됩니다. 성능이 저하됩니다.
Vikas Gautam 2017

4

내 생각은 상태 전이 사이의 상태 그래프에서 경로를 걷고 $stateChangeStart관련된 모든 뷰를 수집하는 것입니다. 그런 다음 모든 ui-view지시문은 해당 뷰가 전환에 관련되어 있는지 확인하고 해당 'ui-resolving'요소에 클래스를 추가합니다 .

plunker의 데모는 두 개의 루트 상태를 소개 first하고 second후자는 두 개의 하위 상태를 가지고 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>

완전한! 감사!
Brynner Ferreira

더 명확하게 설명 할 수는 없습니다. 로딩이 진행되는 동안 ui-view에 무언가를 어떻게 넣는 지 설명 할 수 있습니까? 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은 이후 더 이상 사용되지 않으며 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빈 객체 인 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

누군가를 사용 중이고 다음보기를로드하기 전에 ngRoute대기하고 ui를 resolve사용 angular-bootstrap-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.