Esta es una pregunta de dos partes:
Estoy usando la propiedad resolve dentro de $ stateProvider.state () para capturar ciertos datos del servidor antes de cargar el controlador. ¿Cómo haría para que se muestre una animación de carga durante este proceso?
Tengo estados secundarios que también utilizan la propiedad resolve. El problema es que ui-router parece querer finalizar todas las resoluciones antes de cargar cualquier controlador. ¿Hay alguna forma de que los controladores principales se carguen una vez que se hayan resuelto sus resoluciones, sin tener que esperar a que todas las resoluciones secundarias? Una respuesta a esto probablemente también resolverá el primer problema.
javascript
angularjs
angular-ui-router
Matt Way
fuente
fuente
Respuestas:
EDITAR: Aquí hay una solución aún más fácil, probada y funcionando bien:
En mi controlador principal simplemente tengo
$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(); } });
Esto muestra la ruleta cuando estamos a punto de ir a un estado que tiene algo que resolver y lo oculta, cuando se completa el cambio de estado. Es posible que desee agregar algunas verificaciones de la jerarquía de estados (es decir, también mostrar la ruleta si un estado principal que se está cargando resuelve algo) pero esta solución funciona bien para mí.
Aquí está mi vieja sugerencia como referencia y como alternativa:
En el controlador de su aplicación, escuche el
stateChangeStart
evento y verifique si está a punto de cambiar a un estado en el que desee mostrar una ruleta durante la resolución (consulte https://github.com/angular-ui/ui-router/wiki/Quick -Referencia # 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 } })
Cuando finalmente se llama a su controlador, puede ocultar la ruleta
.controller('StateWithResolveCtrl', function($scope) { $scope.hideSpinner(); })
También es posible que desee verificar los errores que puedan haber ocurrido durante la resolución escuchando el
$stateChangeError
evento y ocultando la animación mientras maneja el error.Esto no está del todo limpio ya que distribuye la lógica de la ruleta entre los controladores, pero es una forma. Espero eso ayude.
fuente
if (toState.resolve)
comprueba si la configuración de estado tiene un diccionario de resolución. En mi caso, esta es una estimación suficientemente buena de que el estado realiza llamadas de servicio asíncronas. Supongo que si hay un bloqueo de resolución, son las llamadas de servicio las que se resuelven. El código muestra y oculta la ruleta solo si se realizaron llamadas de servicio. Como describí anteriormente, es posible que este código deba perfeccionarse para verificar también los estados principales, según su caso de uso.$scope
dentro de una llamada$rootScope
. de donde$scope
viene el de aquishowSpinner
se definió la función.toState.resolve
así que uséfromState.name !== toState.name
. Aparte de eso, tu respuesta es fantástica, ¡bien hecho!Desarrollé la siguiente solución que funciona perfectamente para mí.
1. Agregue la siguiente aplicación.
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. Coloque el indicador de carga justo encima de ui-view. Agregue id = "ui-view" a ui-view div.
<div class="page-loading">Loading...</div> <div ui-view id="ui-view"></div>
3. agregue lo siguiente a su css
.hidden { display: none !important; visibility: hidden !important; }
NOTA:
A. El código anterior mostrará el indicador de carga en dos casos 1) cuando la aplicación angular se carga por primera vez 2) cuando se cambia la vista.
B. Si no desea que se muestre el indicador cuando la aplicación angular se carga por primera vez (antes de que se cargue cualquier vista), agregue una clase oculta al div de carga como se muestra a continuación
<div class="page-loading hidden">Loading...</div>
fuente
run
código no es el Angular Way. En lugar de manipular el DOM allí, establecidos como variables de ámbitoloadingVisible
yviewVisible
, a continuación, en el HTML, imperativamente decir cuando los elementos son visibles u ocultos, como:<div class="page-loading" ng-show="viewVisible">Loading...</div>
. Sin embargo, para vaciar la vista, puede requerir una directiva personalizada.Descubrí que el uso de la barra de carga angular funcionó muy bien para resoluciones largas debido al acceso a la red.
fuente
¿Qué tal agregar contenido al div que será llenado por ui-router una vez que las propiedades se hayan resuelto?
En tus
index.html
<div ui-view class="container"> Loading.... </div>
El usuario ahora verá "Cargando ..." mientras se resuelven las propiedades. Una vez que todo esté listo, el contenido será reemplazado por ui-router con el contenido de sus aplicaciones.
fuente
Utilizo un gif animado que solo muestro cuando
$http
tiene solicitudes pendientes.En mi plantilla de página base, tengo una barra de navegación y un controlador de barra de navegación. La parte relevante del controlador se ve así:
controllers.controller('NavbarCtrl', ['$scope', '$http', function ($scope, $http) { $scope.hasPendingRequests = function () { return $http.pendingRequests.length > 0; }; }]);
El código correspondiente en mi html es:
<span class="navbar-spinner" ng-show="hasPendingRequests()"> <img src="/static/img/spinner.gif"> </span>
¡Espero que eso ayude!
fuente
Mi idea es recorrer el camino en el gráfico de estado entre los estados en transición
$stateChangeStart
y recopilar todas las vistas involucradas. Luego, cadaui-view
directiva observa si la vista correspondiente está involucrada en la transición y agrega'ui-resolving'
clase en su elemento.La demostración de plunker presenta dos estados raíz:
first
ysecond
, el último tiene dos subestadossecond.sub1
ysecond.sub2
. El estadosecond.sub2
también apunta a lafooter
vista que pertenece a su abuelo.fuente
Este es un cargador para globalmente cuando la página navega entre cualquier estado (cualquier página), coloque 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; }) } ])
En html:
<div ng-show="preloader">Loading...</div>
fuente
Prefiero usar una directiva para que coincida con cualquier actividad de carga, principalmente para mantener limpio mi código base
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); } } }]);
fuente
Desde entonces, el uso de
$stateChangeStart
y similares se ha desaprobado y reemplazado con Transition Hooks . Entonces, para la respuesta de Stefan Henze, la versión actualizada sería:$transitions.onStart({}, function(transition) { if (transition.to().resolve) { $scope.showSpinner(); } }); $transitions.onSuccess({}, function(transition) { if (transition.to().resolve) { $scope.hideSpinner(); } });
Puede usar esto en su controlador principal. Recuerda inyectar
$transitions
-.controller('parentController',['$transitions',function($transitions){...}]);
Además, tenga en cuenta que un
resolve
objeto vacío todavía se representarátransition.to().resolve == true
, así que no deje un marcadorresolve
de posición vacío en la declaración de estado.fuente
Mi idea de usar la vista mientras uso resole en el enrutador está funcionando increíble. prueba esto.
//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; }); });
fuente
Si alguien está usando
ngRoute
, esperandoresolve
antes de cargar la siguiente vista y usandoangular-bootstrap-ui
for ui, puede hacer lo siguiente :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; } ] } }); } ]);
fuente