2013-05-02 4 views
6

Ho un ambito angolare contenente due cose:Prestazioni di AngularJS: come aggiornare solo gli ambiti che conosco devono essere aggiornati?

  • un tavolo gigante con 10k righe che prende un secondo per rendere
  • alcune piccolo informazioni extra su di esso in una barra dell'intestazione della mascherina fissa

A seconda di quanto si è spostato verso il basso della pagina/tabella, devo aggiornare uno dei piccoli bit di informazione nell'intestazione; puoi considerarlo come un% di sconto su quanto hai passato.

per ottenere la posizione di scorrimento corrente, ho scritto una direttiva (si può anche trovare here):

app.directive "setWindowScroll", -> 
    restrict: "E" 
    scope: 
    setVariable: "=" 

    link: (scope) -> 
    $(window).scroll -> 

     scope.$apply -> 
     scope.setVariable = $(window).scrollTop() 

Il mio problema è che questo rende lo scorrimento intorno nella tabella u * nsuably lento * . Questo perché il veriable che scrivo con la mia direttiva si trova nelle informazioni extra nello scope, e il cambiamento sembra angolare a dirty-check l'intera tabella per le modifiche quando viene chiamato il mio $apply.

So che questo scorrimento non cambierà mai nulla nella mia tabella e vorrei limitare il mio effetto $apply all'intestazione del mio sito.

Come posso rendere angolare lo non controllare la tabella?

risposta

3

Angolare esegue il controllo sporco in un processo chiamato digest. Quando si effettua una chiamata a $scope.$digest() si propagherà a ogni ambito figlio di $scope. Per notificare le modifiche angolari, di solito fai un $scope.$apply(). Alla fine di $ apply() angular esegue un digest sull'ambito di base, che attiva un digest su ogni ambito dell'applicazione. Quindi, per evitare che il digest entri nel tuo ambito con l'ambito della tabella grande, devi assicurarti che non sia il figlio dell'ambito di informazioni extra, e invece di fare un $ scope. $ Apply() nella tua direttiva potresti fare un $ portata. $ digest(). Questo potrebbe essere un po 'confuso quindi ho fatto un plunker che cerca di mostrare la differenza.Aprire la console e vedere la differenza in caso di scorrimento e scatto del tasto:

http://plnkr.co/edit/45gKhAIt0DrozS7f0Bz2?p=preview

// this will only cause a digest in the current scope and its children 
angular.element($window).bind('scroll',function(){ 
    $scope.scrollY = $window.scrollY; 
    $scope.$digest(); 
}) 

// this will cause a digest in every scope 
angular.element($window).bind('scroll',function(){ 
    $scope.scrollY = $window.scrollY; 
    $scope.$apply(); 
}) 

Quando tutto questo è detto, è una cosa molto insolita da fare - e probabilmente non è una buona idea per un numero Per motivi (l'angolare non si adatta bene a migliaia di elementi, non è possibile utilizzare nessuna delle direttive di evento angolare (ngClick ecc.) perché sono tutti racchiusi in $ apply) - ma se non è possibile eseguire il rendering della tabella sul server lato puoi dare una prova.

+0

Dovevo aprire il plunker in una nuova finestra per ottenere una barra di scorrimento in firefox: http://run.plnkr.co/4CAAfTuXNmgfGdql/ –

+0

Il collegamento alla nuova finestra non funzionava per me. [Questo] (http://beta.plnkr.co/45gKhAIt0DrozS7f0Bz2) e http://run.plnkr.co/plunks/45gKhAIt0DrozS7f0Bz2/ fatto. – nh2

+0

Grazie per la tua grande risposta che lo spiega così bene. Ho messo la mia intestazione nel suo campo di applicazione e in effetti l'uso di '$ digest' invece di' $ apply' risolve i miei problemi. Tuttavia, hai ragione con ciò che dici alla fine - non essere in grado di usare * qualsiasi cosa * che chiama "$ apply" non è fattibile e molto limitante. Dal momento che la mia tabella non cambierà per lo più, proverò a renderizzare la tabella nel browser, ma senza binding di dati (distaccando gli osservatori dopo la creazione, scrivendo una direttiva personalizzata 'ng-repeat'-like, o usando un più primitivo , motore di template non vincolante solo per il tavolo). – nh2

0

Vorrei sapere se è possibile farlo anche, ma - a prescindere - si potrebbe voler considerare di non disegnare l'intera tabella tutto in una volta.

Limita invece (e la quantità di dati/controller associati in Angular-world) solo alle righe visibili in base alla posizione corrente di scorrimento + alcune righe memorizzate nella cache sopra e sotto. È comunque possibile conservare tutti i dati in un array sul client, ma solo esporre un piccolo sottoinsieme ad Angular in qualsiasi momento.

Un modo per farlo potrebbe essere avere un piccolo array in Angular-world che si esegue utilizzando ng-repeat e quindi aggiungere e rimuovere elementi in questo piccolo array angolare dal grande array non angolare basato sulla posizione di scorrimento.

In questo modo Angular conosce solo un piccolo sottoinsieme di dati e dovrebbe eseguire il rendering molto più rapidamente. Penso che questo dovrebbe funzionare abbastanza bene in generale, perché è anche molto meno lavoro per il browser che non ha bisogno di mantenere un albero DOM e il rendering per 10k righe.

+0

In genere hai ragione e ho pensato anche a questo, ma in questo caso ho bisogno di replicare un'interfaccia utente esistente che aveva la stessa tabella, ed è davvero molto scattante se non faccio scattare questo controllo sporco accidentale tutto il tempo aggiornando qualcosa al di fuori del tavolo. – nh2

Problemi correlati