ng-if ou ng-show angular responde lentamente (atraso de 2 segundos?)

87

Estou tentando mostrar ou ocultar um indicador de carregamento em um botão quando uma solicitação está ocupada. Eu faço isso com o angular alterando a variável $ scope.loading quando uma solicitação está carregando ou quando termina de carregar.

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

        })
        .error(function(error){

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

No frontend:

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

Isso funciona bem, mas o ícone de carregamento (atualização de íons) é mostrado por cerca de 2 segundos, enquanto a variável $ scope é atualizada imediatamente. Eu tentei $ scope. $ Apply, mas não parece ser o que está errado aqui, o escopo é atualizado muito bem e imediatamente após a solicitação. É apenas o ícone que não está respondendo rápido o suficiente.

Obrigado por me ajudar a entender isso!

Jorre
fonte
2
Alguma animação envolvida?
tasseKATT
Negativo. Sem animações envolvidas. Em vez disso, usar a classe ng parece ajudar.
Jorre
Estou tendo o mesmo problema ou um problema semelhante. O escopo é atualizado imediatamente e corretamente - eu verifiquei isso registrando mensagens de dentro das $scopefunções que ng-ifusa para descobrir se os elementos relevantes devem ser mostrados. No entanto, os botões ng-ifpermanecem incorretamente visíveis ou ocultos por alguns segundos. Depois de um curto período de tempo, todos os botões assumem seus estados visíveis / ocultos pretendidos. - Eu contornei isso usando ng-hide. Versão Angular 1.2.16.
KajMagnus
Alguma solução para quem não está usando nenhuma animação?
Zia Ul Rehman Mughal

Respostas:

124

Tente remover o ngAnimate se você não o estiver usando na página de configuração e index.html do aplicativo:

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

@Spock; se você ainda precisar usar o ngAnimate, deixe a configuração do aplicativo intocada e apenas adicione o seguinte CSS:

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

Isso ocultará o ícone animado logo depois que sua condição for atendida.

Como você pode ver, estamos definindo .ng-hide-animate como oculto. Isso é o que causa o atraso enquanto espera a conclusão da animação. Você pode adicionar uma animação ao seu evento de ocultação como o nome da classe indica, em vez de ocultá-la como no exemplo acima.

Palvinder
fonte
1
Eu tinha uma página simples com apenas alguns ng-if, ng-showque era visivelmente lenta. Eu removi ngAnimatee resolveu o problema para mim. Obrigado!
Eamonn Gahan
1
Isso resolveu um problema que eu também estava tendo ... Você sabe por que a presença do ngAnimado estava causando a transição lenta?
Clark
Tive o mesmo problema - remover o ngAnimate resolveu .. mas isso não é bom .. muitos módulos precisam do ngAnimate para fazer animações legais .. o que fazer? ngAnimattias onde está você? :)
Spock
21
No caso de ng-if, adicionar apenas .ng-leave { display:none; }ao elemento funcionou para mim ( !importantnão foi necessário).
João
40

Eu tive o mesmo problema e resolvi isso usando ng-class com o nome de classe 'oculto' para ocultar o elemento em vez de usar ng-if ou ng-show / ng-hide.

neimad
fonte
1
Parece relacionado com animações e / ou manipuladores de eventos. Não tenho certeza porque os outros são lentos, mas gostaria de saber
Thiago Festa
1
Como você pode fazer isso?
jsmedmar de
Isso é muito mais rápido! Por que é isso??
Aron
1
Eu acho que isso se deve simplesmente ao fato de que o uso de ngAnimate aplica comportamentos de animação de entrada / saída a elementos usando ng-if / ng-show, enquanto não o faz para alterações em expressões de classe ng.
John Rix
@neimad como isso é feito? No meu caso, preciso usar ng-if para testar se um valor de propriedade é null(que fica por alguns segundos esperando a chamada da API), portanto, dois elementos select são exibidos rapidamente. Então você não está usando ng-if nada? Obrigado.
Chris22
15

Encontrei algumas soluções aqui , mas o melhor para mim foi substituir o estilo da classe .ng-animate:

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

Em 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 é um exemplo: http://jsfiddle.net/9krLr/27/

Espero te ajudar.

Ruben Perez
fonte
9

Eu estava enfrentando um problema semelhante, costumava $scope.$evalAsync()forçar a atualização do vínculo.

Ele funciona como um encanto.

Evite usar $scope.$apply, pois pode entrar em conflito com uma fase $ digest já em execução.

if(selectedStatistics.length === 0 || selectedWorkgroups.length === 0){
    ctrl.isSaveDisabled = true;
    $scope.$evalAsync();
} else{
    ctrl.isSaveDisabled = false;
    $scope.$evalAsync();
}
rach8garg
fonte
Funcionou para mim. Mas isso tem alguma desvantagem?
Sarah Tammam
1
Eu não encontrei nenhum. É muito útil no caso de operações assíncronas.
rach8garg
1
Obrigado pela resposta útil :)
Sarah Tammam
1

Eu tive o mesmo problema ao usar

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

No meu caso, resolvi isso adicionando uma classe:

.hidden {
  display: none;
}

e, em seguida, adicionar a classe condicionalmente em vez de usar *ngIf:

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

Se sempre usar dessa forma, consideraria renomear shouldShowpara shouldHide(e negar a lógica que o atribui), para que possa ser usado como em shouldHidevez de !shouldShow.

Se você tiver display: flexem seu CSS para a classe existente do DIV, essa propriedade de exibição pode ter precedência sobre o display: hidden. Uma solução fácil pode ser usar em display: none !importantvez disso, mas geralmente há soluções melhores para garantir a precedência de outras maneiras. Aqui está uma boa leitura sobre alternativas .

Kent Munthe Caspersen
fonte
0

na versão angular 1.5.x adicionando $scope.$apply()após a mudança na condição fez o trabalho para mim aqui está uma função de exemplo

$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
fonte