template stateprovider otherwise angularjs web-applications angular-ui-router single-page-application

angularjs - stateprovider - Aplicar la rueda de carga durante la resolución del ui-enrutador



ui router angularjs template (6)

Puede usar una directiva que escucha en $routeChangeStart y, por ejemplo, muestra el elemento cuando se activa:

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

Luego lo coloca en el cargador de la vista específica, por ejemplo:

Vista 1:

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

Vista 2:

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

El problema con esta solución (y muchas otras soluciones para el caso) es que si navega hacia una de las rutas desde un sitio externo no habrá una plantilla previa ng-view cargada, por lo que su página podría estar en blanco durante la resolución.

Esto se puede resolver creando una directiva que actuará como un cargador de respaldo. Escuchará $routeChangeStart y mostrará un cargador solo si no hay una ruta previa.

Un ejemplo básico:

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

El cargador de respaldo se colocaría fuera del elemento con ng-view:

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

Demo de todo: http://plnkr.co/edit/7clxvUtuDBKfNmUJdbL3?p=preview

resolve propiedad de resolve de $routeProvider permite ejecutar algunos trabajos ANTES de que se visualice la vista correspondiente.

¿Qué pasa si quiero mostrar una ruleta mientras se ejecutan esos trabajos para aumentar la experiencia del usuario?
De hecho, de lo contrario, el usuario sentiría que la aplicación se ha bloqueado ya que no se visualizaron elementos de vista durante algunos milisegundos> 600, por ejemplo.

Por supuesto, había una forma de definir un elemento div global fuera de la vista actual para mostrarlo con el fin de mostrar el spinner gracias a la función $scope.$rootChangeStart .
Pero no quiero esconder toda la página con solo un pobre girador en el medio.
Quiero que algunas páginas de mi webapp difieran con respecto a la forma en que se muestra la carga.

Me encontré con esta interesante publicación que contiene el problema exacto que describí anteriormente:

Ese enfoque resulta en una horrible experiencia de UI. El usuario hace clic en un botón para actualizar una lista o algo así, y toda la pantalla queda cubierta en un spinner genérico porque la biblioteca no tiene forma de mostrar un spinner solo para la vista (s) que se ve afectada por el cambio de estado. No, gracias.

En cualquier caso, después de archivar este problema, me di cuenta de que la función "resolver" es un anti-patrón. Espera a que se cumplan todas las promesas y luego anima el cambio de estado. Esto es completamente incorrecto: desea que sus animaciones de transición entre estados se ejecuten en paralelo a sus cargas de datos, de modo que este último pueda ser ocultado por el primero.

Por ejemplo, imagine que tiene una lista de elementos, y al hacer clic en uno de ellos, se oculta la lista y se muestran los detalles del elemento en una vista diferente. Si tenemos una carga asíncrona para los detalles del artículo que toma, en promedio, 400 ms, podemos cubrir la carga casi en su totalidad con una animación "leave" de 300 ms en la vista de lista y una animación "enter" de 300 ms. en la vista de detalles del elemento. De esta forma, proporcionamos una sensación más pulida a la interfaz de usuario y podemos evitar mostrar una ruleta en la mayoría de los casos.

Sin embargo, esto requiere que iniciemos la carga asíncrona y la animación de cambio de estado en el mismo momento. Si utilizamos "resolver", entonces toda la animación asíncrona ocurre antes de que comience la animación. El usuario hace clic, ve una rueda giratoria y luego ve la animación de transición. El cambio de estado completo tomará ~ 1000ms, que es demasiado lento.

"Resolver" podría ser una forma útil de almacenar en caché las dependencias entre diferentes vistas si tuviera la opción de no esperar en las promesas, pero el comportamiento actual, de resolverlas siempre antes de que comience el cambio de estado, lo hace casi inútil, IMO. Debe evitarse para cualquier dependencia que implique cargas asíncronas.

¿Debería dejar de usar resolve para cargar algunos datos y comenzar a cargarlos directamente en el controlador correspondiente? De modo que puedo actualizar la vista correspondiente siempre que se ejecute el trabajo y en el lugar que quiero en la vista, no globalmente.


creo que esto es bastante limpio

app.run([''$rootScope'', ''$state'',function($rootScope, $state){ $rootScope.$on(''$stateChangeStart'',function(){ $rootScope.stateIsLoading = true; }); $rootScope.$on(''$stateChangeSuccess'',function(){ $rootScope.stateIsLoading = false; }); }]);

y luego a la vista

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


Para seguir con la respuesta de Pranay, así es como lo hice.

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>


Tengo dos años de retraso en esto, y sí, estas otras soluciones funcionan, pero me resulta más fácil simplemente manejar todo esto en un solo bloque de ejecución como tal

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

no necesitas nada más Bastante fácil eh. he aquí un ejemplo de ello en acción http://plnkr.co/edit/tggNzKlHMc9OwAnBq6eA?p=preview


El problema con ''$ stateChangeStart'' y ''$ stateChangeSuccess'' es "$ rootScope.stateIsLoading" no se actualiza cuando vuelve al último estado. Hay alguna solucion sobre eso? También utilicé:

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

y

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

pero hay el mismo problema.