2015-06-19 17 views
15

Ho una pagina con alcune schede e ogni scheda ha una grande quantità di collegamenti angularjs. This è la pagina di esempio in cui sono di fronte al problema. Ogni scheda richiede circa 10 secondi per il rendering.Scheda Angularjs Caricamento spinner durante il rendering

Quindi ho pianificato di fornire uno spinner di caricamento durante il rendering delle schede. Quindi ho programmato di mostrare lo spinner di caricamento durante il clic sulla linguetta e rimuovere la ghiera alla fine ($last) della ng-repeat.

Nel ng-click sulla scheda ho attivato la filatura loader

<ul> 
    <li ng-repeat="tab in tabs" 
     ng-class="{active:isActiveTab(tab.url)}" 
     ng-click="onClickTab(tab)">{{tab.title}} 
    </li> 
</ul> 

controller

$scope.onClickTab = function (tab) { 
    showLoader(); 
    $scope.currentTab = tab.url; 
} 

Per controllare ng-repeat è completa ho usato qui di seguito direttiva

.directive('onFinishRender', function ($timeout) {  
    return { 
     restrict: 'A', 
     link: function (scope, element, attr) { 
      if (scope.$last === true) { 

       $timeout(function() { 
        scope.$emit('ngRepeatFinished'); 
       }); 
      } 
     } 
    } 
}); 

$scope.$on('ngRepeatFinished', function (ngRepeatFinishedEvent) { 
     removeLoader(); 
}); 

showLoader e removeLoader sono semplici funzioni che aggiungono e rimuovono il div avendo un semplice spin di caricamento ner.

function showLoader() { 
    $('body').append('<div class="loader"></div>'); 
} 

function removeLoader() { 
    $('.loader').fadeOut(function() { 
     $(this).remove(); 
    }); 
} 

Risultato atteso:. Loader Spinning da visualizzare quando si fa clic sulla scheda e appaiono fino finiture ng-repeat (cioè la scheda cliccato rende completamente)

risultato effettivo: Il caricatore non viene visualizzato quando si fa clic su scheda e appare quasi alla fine di ng-repaet e appare per una frazione di secondi. Here è possibile osservare il suddetto comportamento. Penso che la pagina non sia in grado di mostrare lo spinner a causa del processo di binding angolare che rende il congelamento della pagina.

Qualcuno può aiutarmi a risolvere questo?

+0

La mia risposta è corretta perché è originata da un thread SO scritto dall'autore di Angular che affronta questo esatto problema con lo sfarfallio dello schermo in fase di caricamento a causa di associazioni di dati.Oltre alle 2 soluzioni che ti ho offerto, ti suggerisco anche di utilizzare i binding di dati a 1 via nei punti in cui non hai bisogno di un binding di dati a 2 vie (se la maggior parte delle persone come te ha bisogno solo di un binding di dati a 1 via attraverso la maggior parte della tua app se non di tutte esso). Questo può essere fatto facilmente includendo un doppio colon all'interno delle espressioni legate ai dati come {{:: member.name}} –

+0

Demo di lavoro e, soprattutto, la soluzione corretta aggiunta alla mia risposta. Rinuncia a quella taglia! –

+1

Penso che OP possa leggere tutte le risposte e poi decidere quale attribuire la taglia (se esiste) tutto da solo? –

risposta

12

È possibile modificare il codice in questo modo:

$timeout(function() { 
    $scope.currentTab = tab.url 
}, 100); 

Demo: http://jsfiddle.net/eRGT8/1053/

quello che faccio è, spingo currentTab passare alla successiva Dige st ciclo. (È una specie di trucco, non una soluzione di cui sono orgoglioso.) Pertanto, nel primo ciclo di digerimento (prima del $timeout richiamato) viene mostrato solo il caricamento. Nel prossimo ciclo di digest, la procedura di blocco ng-repeat inizia a funzionare ma, poiché rendiamo il caricamento visibile in precedenza, viene visualizzato.

:)

Questo risolve il problema, ma in esecuzione lunga corsa e bloccando javascript che pende completamente il browser non è una buona esperienza utente. Inoltre, poiché il browser si blocca completamente, la gif di caricamento non si anima, solo sarà visibile.

+3

È possibile utilizzare le animazioni CSS per il caricatore anziché una GIF animata e il caricatore si animerà anche durante il rendering. Inoltre non è necessario impostare un ritardo di 100 ms, solo un timeout di $ senza ritardo farà il trucco. Controlla: http://jsfiddle.net/v65kqygf/ – tomtastico

+0

Mentre questo è un po 'un trucco/soluzione alternativa, è l'approccio più semplice per garantire la visualizzazione dello spinner prima che inizi il rendering. La mia gif si blocca anche se così cambierà in CSS. – EdL

+0

@Umut Benzer è possibile mostrare spinner come questo in ogni scheda. Nella demo che hai fornito se cambio tra le schede poi congela lo schermo fino al completamento del caricamento e non riesco a tornare ad altre schede che sono già state caricate. Stavo cercando la stessa funzionalità, solo che è necessario in ogni scheda in modo indipendente in modo che io possa passare tra le schede liberamente. Potresti fornire alcuni suggerimenti su questo, è possibile o no. –

1

Solitamente risolvo questo nel mio codice HTML usando ng-if.

Nel tuo caso questo sarebbe diventato qualcosa di simile:

<ul> 
    <span ng-if="!tabs">Content loading...</span> 
    <span ng-if="tabs && tabs.length < 1">No tabs available</span> 
    <span ng-if="tabs && tabs.length > 0"> 
    <li ng-repeat="tab in tabs" 
     ng-class="{active:isActiveTab(tab.url)}" 
     ng-click="onClickTab(tab)">{{tab.title}} 
    </li> 
    </span> 
</ul> 
+0

Non ha risolto il problema. qui c'è il violino che mostra la tua soluzione http://jsfiddle.net/eRGT8/1046/ Il problema è che la trottola di caricamento non viene visualizzata mentre la scheda è in rendering. – Navaneet

+0

Devi applicare questa logica al contenuto della scheda :) Scusa se ho frainteso. – Carsten

+0

Cosa intendi applicando questa logica sul contenuto della scheda? Non ti ho preso. – Navaneet

1

penso che non è posibble mostrare carico filatore mentre rendring pagina. Il motore di rendering è a thread singolo. Quasi tutto, tranne le operazioni di rete, avviene in un singolo thread. In Firefox e Safari questo è il thread principale del browser. In chrome è il thread principale del processo di tabulazione. Le operazioni di rete possono essere eseguite da più thread paralleli.

È possibile leggere aboout che here

C'è un solo modo per risolvere questo problema - di ridisegnare la pagina: per mostrare solo una parte dei dati, lazy loading o ecc

+0

Non chiede caricatore quando la pagina viene caricata ma per uno spinner durante il rendering del contenuto di una scheda all'interno della pagina (questa domanda è specifica per l'angolare) Sono abbastanza sicuro che ci debba essere un modo e mi sono imbattuto in un [simile problema] (http://stackoverflow.com/questions/30899334/loading-a-template-in-angular-routeprovider-asynchronously) –

+0

Ovviamente è possibile caricare i modelli in modo asincrono, ma mentre il browser esegue il rendering del contenuto (funziona angolare) lo spinner non funziona comunque – Mikalai

0

È possibile effettuare direttiva personalizzato per ogni esempio li carico-counter, quindi aggiungere servizio di dipendenza che terrà banco, e in ogni direttiva all'interno funzione di collegamento è possibile chiamare

var app = angular.module('app', []); 
 

 
app.controller('List', function(LoadingCounter) { 
 

 
    this.tabs = [{ 
 
    title: "Test tab", 
 
    url: "http://google.sk" 
 
    }, { 
 
    title: "Test tab", 
 
    url: "http://google.sk" 
 
    }, { 
 
    title: "Test tab", 
 
    url: "http://google.sk" 
 
    }] 
 

 
    this.LoadingCounter = LoadingCounter; 
 

 
}); 
 

 
app.directive('spinningLoader', function(LoadingCounter) { 
 
    return { 
 
    restrict: 'A', 
 
    scope: { 
 
     max: "=" 
 
    }, 
 
    link: function(scope) { 
 
     LoadingCounter.updateCounter(scope.max); 
 
    } 
 
    } 
 
}); 
 

 
app.factory('LoadingCounter', function($timeout) { 
 
    var service = {}; 
 

 
    service.currentCounter = 0; 
 
    service.finished = false; 
 
    service.updateCounter = function(max) { 
 
    service.currentCounter++; 
 
    if (service.currentCounter == max) { 
 
     //example timeout 
 
     $timeout(function() { 
 
     service.finished = true; 
 
     }, 2000); 
 

 
    } 
 
    } 
 
    service.isFinished = function() { 
 
    return service.finished; 
 

 
    } 
 
    return service; 
 
});
.hidden { 
 
    display: none; 
 
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script> 
 

 
<body ng-app="app" ng-controller="List as _"> 
 
    <div ng-if="!_.LoadingCounter.isFinished()">Loading</div> 
 
    <ul ng-class="{'hidden' : !_.LoadingCounter.isFinished() }"> 
 
    <li spinning-loader max="_.tabs.length" ng-repeat="tab in _.tabs" ng-class="{active:isActiveTab(tab.url)}" ng-click="onClickTab(tab)">{{tab.title}} 
 
    </li> 
 
    </ul> 
 
</body>

6

I Raccomanderei FORTEMENTE di leggere prima questo thread SO, scritto dall'autore di AngularJS stesso che risolve il problema di root.

Delaying AngularJS route change until model loaded to prevent flicker

Misko chiede poi risponde alla sua stessa domanda per quanto riguarda questo tema suggerendo l'uso di $ routeProvider, in questo modo:

$routeProvider. 
     when('/phones', { 
     templateUrl: 'partials/phone-list.html', 
     controller: PhoneListCtrl, 
     resolve: PhoneListCtrl.resolve}). // <-- this is the connection when resolved 
     when('/phones/:phoneId', { 
     templateUrl: 'partials/phone-detail.html', 
     controller: PhoneDetailCtrl, 
     resolve: PhoneDetailCtrl.resolve}). 
     otherwise({redirectTo: '/phones'}); 

Qui è il prodotto/soluzione completa che stai cercando, che è una versione migliorata del AngularJS telefono demo:

http://mhevery.github.io/angular-phonecat/app/#/phones

Questo requ ires NgRoute(), ma è l'approccio corretto. L'uso di un timeout di $ qui mi sembra un po 'hacky - Non sto dicendo che hai bisogno di ngRoute, ma non sono sicuro di come rimuoverlo senza (e mi rendo conto che le tue schede potrebbero non essere instradate al momento ma spero che sia d'aiuto comunque) .

Inoltre, può anche trovare utilizzando angular.element (document) .ready (function() {}); per il tuo carico utile iniziale risolverà anche le tue woah.

angular.element(document).ready(function(){ 
    $('.loading').remove(); // Just an example dont modify the dom outside of a directive! 
    alert('Loaded!'); 
}); 

Come si può vedere nessun timeout $ (function() {}); è usato entrambi gli approcci, qui è la prova del angular.element.ready() funziona - senza un router:

LAVORO DEMO http://codepen.io/nicholasabrams/pen/MwOMNR

* se mi reaaally necessario posso fare alcune schede up ma non hai fornito un esempio di codice funzionante nel tuo post, quindi ho usato un'altra demo che ho creato per un altro thread per mostrare la soluzione.

QUI È ANCORA UN'ALTRA SOLUZIONE, questa volta ho usato il tuo esempio!

http://jsfiddle.net/eRGT8/1069/

Questa volta ho il fuoco in funzione quando l'indice $ === $ scorso (ultimo elemento ngRepeat()) in questo modo il caricatore viene rimosso dal Dom o nascosto quando la ripetizione è finito di fare il suo lavoro)

+0

Non riesco davvero a credere a quante persone stiano sostenendo di rallentare l'app con un timeout $ solo per farlo sembrare come se l'app si stesse caricando. Non suona qualcosa di sbagliato in questo? –

Problemi correlati