2013-04-10 3 views
11

Sto provando ad usare una direttiva JS Angolare per centrare il contenuto di un div.Come richiamare una funzione in una direttiva Angular dopo che i nodi figli sono stati elaborati?

Questa è la versione semplificata del modello:

<div id="mosaic" class="span12 clearfix" s-center-content> 
    <div ng-repeat="item in hits" id="{{item._id}}" > 
    </div> 
</div> 

Questa è la direttiva:

module.directive('sCenterContent', function() { 
    var refresh = function(element){ 
     var aWidth= element.innerWidth(); 
     var cWidth= element.children()[0].offsetWidth; 
     var cHeight= element.children()[0].offsetHeight; 
     var perRow= Math.floor(aWidth/cWidth); 
     var margin=(aWidth-perRow*cWidth)/2; 
     if(perRow==0){ 
      perRow=1; 
     } 
     var top=0; 
     element.children().each(function(index, child){ 
      $(child).css('margin-left','0px'); 
      if((index % perRow)==0){ 
       $(child).css('margin-left',margin+'px'); 
      } 
     }); 
    }; 
    return { 
     link : function(scope, element, attrs) { 
      $(window).resize(function(event){ 
       refresh(element); 
      }); 
     } 
    }; 
}); 

Galleggia fondamentalmente alcuni div interni e aggiunge un margine alla prima div in ogni riga, quindi il contenuto è centrato.

Questo funziona correttamente quando il browser viene ridimensionato. Il problema si presenta quando provo a fare un aggiornamento subito dopo l'inizializzazione.

Ho provato con la funzione di collegamento post come visto in this question. Le funzioni pre link e post link vengono richiamate nell'ordine previsto ma non dopo che i figli di elemenet con la direttiva s-center-content vengono visualizzati. La chiamata all'aggiornamento fallisce poiché non vengono trovati bambini.

Come posso effettuare una chiamata per l'aggiornamento assicurando che i bambini siano stati elaborati?

risposta

1

Finalmente fatto!

Ho usato livequery plug-in e ha aggiunto la seguente riga alla fine del metodo link:

$('*[s-center-content] > *').livequery(function(event){ 
    refresh(element); 
}); 

ho selezionate una prima misura di bambino (attuali e futuri) dell'elemento con la direttiva s-center-content e allegare un gestore viene chiamato quando gli elementi vengono aggiunti al DOM.

EDIT

Una nota finale. Il layout dei miei contenuti dipende principalmente sulle immagini interiori con valutati in modo dinamico src attributi. Quindi, per farlo funzionare ho dovuto cambiare il codice a questo:

$('*[s-center-content] img').livequery(function(event){ 
    $(this).load(function(){ 
     refresh(element);      
    }); 
}); 

il codice completo della direttiva che stabilisce div galleggianti Sinistra Centra è la seguente. Si noti che si basa sull'aggiunta di un margine calcolato al primo div nella riga.

module.directive('sCenterContent', function() { 
    var refresh = function(element){ 
     var aWidth= element.innerWidth(); 
     var cWidth= element.children()[0].offsetWidth; 
     var cHeight= element.children()[0].offsetHeight; 
     var perRow= Math.floor(aWidth/cWidth); 
     var margin=(aWidth-perRow*cWidth)/2; 
     if(perRow==0){ 
      perRow=1; 
     } 
     var top=0; 
     element.children().each(function(index, child){ 
      $(child).css('margin-left','0px'); 
      if((index % perRow)==0){ 
       $(child).css('margin-left',margin+'px'); 
      } 
     }); 
    }; 
    return { 
     link : function(scope, element, attrs) { 
      $(window).resize(function(event){ 
       refresh(element); 
      }); 
      $('*[s-center-content] img').livequery(function(event){ 
       $(this).load(function(){ 
        refresh(element);     
       }); 
      }); 
     } 
    }; 
}); 
+1

Ben fatto, questo è molto utile! Proverò a trovare il thread del gruppo di google e il problema in modo angolare che parla di questo problema e farò riferimento alla tua risposta. –

+0

Grazie per lo sforzo Roy. –

+4

Se guardate i plugin [code] (https://github.com/brandonaaron/livequery/blob/master/jquery.livequery.js), si noterà che ha usato anche 'setTimeout (fn, 20)' per abbina nuovi elementi. Quindi è una soluzione sporca in 'pacchetto' vista amichevole. – FelikZ

5

Si tratta di un problema ricorrente (Da qualche parte c'è un problema associato a questa, così come un thread nel forum di Google ..)

angolare non ha modo di sapere quando il DOM è finito, si può conoscere solo quando le funzioni che chiama ritornano (e tutte le promesse sono risolte). Se tali funzioni sono asincrone e non hanno una callback o una promessa, non c'è modo di essere avvisati.

Il modo in cui ho visto fare (e il modo in cui l'ho fatto) è con un setInterval che controlla il DOM periodicamente per vedere se è in uno stato completato. Odio questa soluzione e sarò grato per un'alternativa, ma, fa il lavoro.

+0

Che dire guardando l'elemento padre? sarei avvisato con aggiunte di bambini? Se è così, quello potrebbe essere il modo. Controllerò! –

7

Credo che usando di angolare $watch è una migliore, soluzione più elegante:

link : function(scope, element, attrs) { 
      $(window).resize(function(event){ 
       refresh(element); 
      }); 

      var watchCount = 0, 
      unWatcher = $watch(
       function() { 
        return $('*[s-center-content] img').length !== 0; 
       }, 
       function() { 
        // ensure refresh isn't called the first time (before the count of elements is > 0) 
        if (++watchCount === 1) { 
         return; 
        } 

        refresh(element);     

        // stop watching the element now that refresh has been called 
        unWatcher(); 
       }, true); 
     } 

A proposito di smettere di guardare: AngularJS : Clear $watch

+1

Questo è molto intelligente - forse un po 'più di descrizione sulla risposta renderà più visibile. –

+0

Questo è piuttosto fresco. Potrebbe voler guardare solo su $ (selector) .length Anche se non è del tutto sicuro se funziona sempre. – David

0

Credo che questo può essere realizzato con i CSS. Gli elementi centrati sull'obiettivo sono indipendentemente dal numero come questo?

[] [] [] [] [] 

    [] [] [] 

Se è così, questo è sicuramente possibile con css e potrebbe essere realizzato con meno complessità. Ecco un esempio:

http://codepen.io/thetallweeks/pen/kvAJb