2016-05-20 13 views
13

Quello che sto facendo
Ho un componente che nasconde/mostra utilizzando *ngIf sulla base di un semplice booleano. Quando il componente diventa visibile, voglio applicare lo stato attivo a un elemento figlio all'interno del suo modello.Come verificare se ngIf ha preso effetto

Il problema
Se I flip il valore booleano della componente mostra correttamente, ma se poi cercare di ottenere un riferimento all'elemento figlio utilizzando this._elRef.nativeElement.querySelector("div#test") si tratta appena tornato null. Se aspetto qualche secondo, lo stesso codice restituirà il riferimento all'elemento come mi aspettavo.

speculazione
Sto assumendo che dopo lanciando l'angolare booleano passa attraverso un ciclo di rendering intera per rivelare il componente appena visibile e che questo non ha finito per il momento ho applicare la querySelector() nella riga successiva.

Quello che mi piacerebbe sapere
Quindi quello che mi chiedo è, come posso essere sicuro che il mio ngIf ha avuto effetto e gli elementi sono loro nel DOM di essere selezionati?
Esiste un callback per ngIf o posso forzare la visualizzazione per aggiornare e ottenere una richiamata da quello?

Spero che abbia senso. È stata una lunga giornata (lunga settimana) e sono super stanco.
Grazie a tutti

se aiuta, sto usando Angular2 v2.0.0-beta.15

risposta

16

Se si capovolgere il valore booleano per true e nella prossima riga di codice si tenta di ottenere un riferimento al componente o elemento DOM controllato da NgIf ... beh, quel componente o elemento DOM non esiste ancora. Angolare non funziona in parallelo con il tuo codice. Il callback JavaScript deve terminare, quindi viene eseguito il controllo Angolare (rilevamento modifiche), che noterà il cambiamento del valore booleano e creerà il componente o l'elemento DOM e lo inserirà nel DOM.

Per risolvere il problema, chiamare setTimeout(callbackFn, 0) dopo aver invertito il valore booleano. Questo aggiunge il tuo callbackFn allo JavaScript message queue. Ciò garantirà che Angular (rilevamento modifiche) venga eseguito prima della funzione di callback. Quindi, quando si esegue callbackFn, l'elemento che si desidera mettere a fuoco dovrebbe esistere. L'utilizzo di setTimeout(..., 0) assicura che il tuo callbackFn venga chiamato nel turno successivo di JavaScript event loop.

Questa tecnica di utilizzo di setTimeout(..., 0) viene utilizzata nella guida degli utili di LifeCycle dev quando si discute di AfterView* hooks.

Ecco alcuni altri esempi, se avete bisogno di ulteriori dettagli:

+0

Quanto robusto è il() '' soluzione setTimeout? Ha risolto il problema usando un ritardo di 0, ma questo sembra un po 'hacky. Il timeout non è completamente separato dal resto del codice e quindi non potrebbero esserci momenti in cui gli aggiornamenti delle visualizzazioni richiedono più tempo per il rendering e il ritardo dovrebbe essere aumentato? – popClingwrap

+0

@popClingwrap, vedere le mie modifiche di risposta. Ho provato ad aggiungere ulteriori dettagli. Usare 'setTimeout()' non è affatto hacky ed è robusto. È il normale metodo JavaScript per rinviare l'esecuzione di alcuni codici. In questo caso, è necessario posticipare la messa a fuoco fino a quando Angular crea l'elemento DOM. Non dovrebbero esserci casi "più lunghi da rendere". Dovrebbe sempre essere un giro del ciclo degli eventi per rendere qualsiasi modifica del DOM apportata dal rilevamento del cambiamento angolare. –

+0

Queste sono alcune cose interessanti che non sapevo prima riguardo al vero coraggio di Javascript. Grazie per i dettagli aggiunti, mi ha davvero aiutato e ho dato un sacco di nuove cose su cui leggere e diventare confuso da :) – popClingwrap

4

Questa domanda è abbastanza vecchia e la soluzione corrente potrebbe non essere disponibile in quel momento.

Il metodo setTimeout() è perfettamente funzionante, ma ha uno svantaggio significativo. Se si imposta semplicemente una classe per posizionare un elemento, come ho fatto io, si ottiene un risultato saltuario, poiché il codice viene eseguito dopo il ciclo angolare.

L'utilizzo di ChangeDetectorRef produce un risultato che non salta.

Così, invece di questo:

class Foo { 
    public isDisplayed = false; 

    constructor(@Inject(ElementRef) private elementRef: ElementRef) { 
    } 

    public someMethod(): void { 
    this.isDisplayed = true; 
    setTimeout(() => { 
     const child = this.elementRef.nativeElement.querySelector('.child-element'); 
     // ... 
    }); 
    } 
} 

Si potrebbe fare questo:

class Foo { 
    public isDisplayed = false; 

    constructor(@Inject(ElementRef) private elementRef: ElementRef, 
       @Inject(ChangeDetectorRef) private changeDetectorRef: ChangeDetectorRef) { 
    } 

    public someMethod(): void { 
    this.isDisplayed = true; 
    this.changeDetectorRef.detectChanges(); 
    const child = this.elementRef.nativeElement.querySelector('.child-element'); 
    // ... 
    } 
} 
+0

Molto bello. È possibile attivare manualmente il rilevamento delle modifiche angolari, quindi posizionare il proprio elemento - tutto prima che il browser venga effettivamente visualizzato. –

Problemi correlati