angular ng-if o ng-show responde lento (¿retraso de 2 segundos?)

87

Estoy tratando de mostrar u ocultar un indicador de carga en un botón cuando una solicitud está ocupada. Hago eso con angular cambiando la variable $ scope.loading cuando se carga una solicitud o cuando se termina de cargar.

 $scope.login = function(){
     $scope.loading = true;
    apiFactory.getToken()
        .success(function(data){

        })
        .error(function(error){

        })
         .finally(function(){
               $timeout(function() {
                 $scope.loading = false;
               }, 0);
         });
 };

En la interfaz:

<button ng-disabled="loading" class="button button-outline button-positive" type="submit">
Log in 
<span ng-if="loading" class="ion-refreshing"></span>
</button>

Esto funciona bien, pero el ícono de carga (actualización de iones) se muestra durante aproximadamente 2 segundos, mientras que la variable $ scope se actualiza inmediatamente. Intenté $ scope. $ Apply pero eso no parece ser lo que está mal aquí, el alcance se actualiza bien e inmediatamente después de la solicitud. Es solo el ícono que no responde lo suficientemente rápido.

¡Gracias por ayudarme a entender esto!

Jorre
fuente
2
¿Alguna animación involucrada?
tasseKATT
Negativo. No hay animaciones involucradas. En cambio, usar ng-class parece ayudar.
Jorre
Tengo el mismo problema o uno similar. El alcance se actualiza de forma inmediata y correcta; lo verifiqué registrando mensajes dentro de las $scopefunciones que ng-ifutiliza para averiguar si se deben mostrar los elementos relevantes. Sin embargo, los botones con ng-ifpermanecen incorrectamente visibles u ocultos durante un segundo. Luego, después de un rato, todos los botones toman sus estados visibles / ocultos previstos. - Trabajé alrededor de esto usando en su ng-hidelugar. Versión angular 1.2.16.
KajMagnus
¿Alguna solución para aquellos que no utilizan animaciones?
Zia Ul Rehman Mughal

Respuestas:

124

Intente eliminar ngAnimate si no lo está usando desde la configuración de su aplicación y la página index.html:

angular.module('myApp', [...'ngAnimate',...])

@Spock; Si aún necesita el uso de ngAnimate, deje intacta la configuración de su aplicación y simplemente agregue el siguiente CSS:

.ng-hide.ng-hide-animate{
     display: none !important;
}

Eso ocultará el icono animado inmediatamente después de que se cumpla su condición.

Como puede ver, estamos configurando .ng-hide-animate como oculto. Esto es lo que causa el retraso mientras espera a que se complete la animación. Puede agregar una animación a su evento hide como lo indica el nombre de la clase en lugar de ocultarlo como en el ejemplo anterior.

Palvinder
fuente
1
Tenía una página simple con solo un par de ng-if, ng-showque era visiblemente lenta. Quité ngAnimatey solucionó el problema por mí. ¡Gracias!
Eamonn Gahan
1
Esto resolvió un problema que también estaba teniendo ... ¿Sabes por qué la presencia de ngAnimate estaba causando la transición lenta?
Clark
Tuve el mismo problema: eliminar ngAnimate lo resolvió ... pero esto no es bueno ... muchos módulos necesitan ngAnimate para hacer animaciones geniales ... ¿qué hacer? ngAnimattias ¿dónde estás? :)
Spock
21
En el caso de ng-if, agregar solo .ng-leave { display:none; }al elemento funcionó para mí ( !importantno era necesario).
Joao
40

Tuve el mismo problema y lo solucioné usando ng-class con el nombre de clase 'oculto' para ocultar el elemento en lugar de usar ng-if o ng-show / ng-hide.

neimad
fuente
1
Parece relacionado con animaciones y / o controladores de eventos. No estoy realmente seguro de por qué los demás son lentos, pero me gustaría saberlo
Thiago Festa
1
¿cómo puedes hacer esto?
jsmedmar
¡Esto es mucho más rápido! ¿¿Por qué es esto??
Aron
1
Creo que esto se debe simplemente al hecho de que el uso de ngAnimate aplica comportamientos de animación de entrada / salida a los elementos que usan ng-if / ng-show, mientras que no lo hace para los cambios en las expresiones de la clase ng.
John Rix
@neimad ¿cómo se hace esto? En mi caso, necesito usar ng-if para probar si el valor de una propiedad es null(que es durante un par de segundos esperando la llamada a la API), por lo que dos elementos seleccionados se muestran brevemente. Entonces, ¿no estás usando ng-if nada? Gracias.
Chris 22 de
15

Encontré algunas soluciones aquí , pero lo mejor para mí fue anular el estilo de la clase .ng-animate:

.ng-animate.no-animate {
    transition: 0s none;
    -webkit-transition: 0s none;
    animation: 0s none;
    -webkit-animation: 0s none;
}

En html:

<button ng-disabled="loading" class="button button-outline button-positive" type="submit">
    Log in 
    <span ng-if="loading" class="ion-refreshing no-animate"></span>
</button>

Este es un ejemplo: http://jsfiddle.net/9krLr/27/

Espero ayudarte.

Ruben perez
fuente
10

Estaba enfrentando un problema similar, solía $scope.$evalAsync()forzar la actualización del enlace.

Funciona a las mil maravillas.

Evite el uso, $scope.$applyya que puede entrar en conflicto con una fase $ digest que ya se está ejecutando.

if(selectedStatistics.length === 0 || selectedWorkgroups.length === 0){
    ctrl.isSaveDisabled = true;
    $scope.$evalAsync();
} else{
    ctrl.isSaveDisabled = false;
    $scope.$evalAsync();
}
rach8garg
fuente
Trabajó para mi. ¿Pero tiene alguna desventaja?
Sarah Tammam
1
No he encontrado ninguno. Es muy útil en caso de operaciones asincrónicas.
rach8garg
1
Gracias por la útil respuesta :)
Sarah Tammam
Esta respuesta parece ser un premio gordo, gracias.
Abdeali Chandanwala
Por cierto, este problema retrasado ocurre principalmente en el entorno del
host local
1

Tuve el mismo problema al usar

<div *ngIf='shouldShow'>
    <!-- Rest of DIV content here -->
</div>

En mi caso lo resolví agregando una clase:

.hidden {
  display: none;
}

y luego agregar la clase condicionalmente en lugar de usar *ngIf:

<div [ngClass]="{'hidden': !shouldShow}">
    <!-- Rest of DIV content here -->
</div>

Si siempre lo uso de esta manera, consideraría cambiar el nombre shouldShowa shouldHide(y negar la lógica que lo asigna), para que pueda usarse como en shouldHidelugar de !shouldShow.

Si tiene display: flexen su CSS para la clase existente de DIV, esa propiedad de visualización podría tener prioridad sobre display: hidden. En su display: none !importantlugar, puede ser una solución fácil , pero a menudo hay mejores soluciones para garantizar la precedencia de otras formas. Aquí hay una buena lectura sobre alternativas .

Kent Munthe Caspersen
fuente
0

en la versión angular 1.5.x agregando $scope.$apply()después de que el cambio en la condición hizo el trabajo para mí, aquí hay una función de ejemplo

$scope.addSample = function(PDF)
        {
            var validTypes ="application/pdf";
            if(PDF.type == validTypes)
            {
                //checking if the type is Pdf and only pdf
                $scope.samplePDF= PDF.files[0];
                $scope.validError = false;
                $scope.$apply();
            }

            else
            {
                 $scope.validError = true;
                 $scope.samplePDF= null;
                 $scope.$apply();
            }


        }
mohamed
fuente