2013-04-17 4 views
30

Ho un orologio che fa scattare un evento DOM:

scope.$watch(function() { return controller.selected; }, function(selected) { 
    if (selected) { 
     $input.trigger('focus'); 
    } 
}); 

Il problema è che ho un gestore su 'focus' che fa un scope.$apply.

$input.bind('focus', function() { 
    scope.$apply(function() { controller.focused = true; }); 
}); 

Così, quando il mio $watch viene sparato dall'interno di una $digest che causa un errore perché cerca di innescare un altro $digest.

La soluzione temporanea che ho è di mettere il trigger in un $timeout.

scope.$watch(function() { return controller.selected; }, function(selected) { 
    if (selected) { 
     $timeout(function() { $input.trigger('focus'); }); 
    } 
}); 

Questo funziona ... finora. È questo il modo corretto di gestirlo? Non sono sicuro se questo catturi ogni caso e vorrei vedere se c'è un modo approvato angolare per avere un pezzo di codice di rinvio dopo il digest.

Grazie!

+0

$ input.trigger ('focus') -> sta cercando di impostare lo stato attivo su un elemento di input? – ganaraj

+0

sì ... sembra che io possa semplicemente fare '$ input.focus()'. – anonymous

risposta

64

$timeout è normalmente ciò che viene utilizzato per eseguire qualcosa dopo un ciclo di digest (e dopo il rendering del browser).

$timeout causerà un altro ciclo di digest da eseguire dopo l'esecuzione della funzione. Se il tuo trigger non ha alcun effetto su Angolare, puoi impostare l'argomento invokeApply su false per evitare di eseguire un altro ciclo di digest.

Se si desidera che il callback da eseguire prima di il browser esegue il rendering: Se il codice è in coda con $evalAsyncda una direttiva, dovrebbe funzionare dopo il DOM è stato manipolato da angolare, ma prima che il browser rende. Tuttavia, se il codice viene accodato utilizzando $evalAsyncda un controller, verrà eseguito prima che il DOM sia stato manipolato da Angular (e prima del rendering del browser). Vedi anche https://stackoverflow.com/a/17303759/215945.

+0

"' $ timeout' causerà l'esecuzione di un altro ciclo di digest. " ... strano Penserei che se fosse così e il mio gestore di eventi sta chiamando 'apply', avrei lo stesso errore 'già in $ digest'. – anonymous

+0

Oh okay, chiama '$ apply' dopo che la funzione è stata eseguita. Quindi dovrei impostare l'argomento 'invokeApply' su false per evitare un' $ digest 'inutile. Grazie. – anonymous

+0

@eyston, ho dimenticato l'argomento 'invokeApply'. Aggiornerò la mia risposta con quella invece di 'setTimeout'. –

0

Come tutti continuano a dire, non dovresti fare roba DOM nel controller.

Ecco una soluzione che applica il binding dei dati bidirezionale per mettere a fuoco. Ora il tuo focus è legato a una variabile. Quindi, quando si imposta la variabile su true, si concentra sull'elemento corrispondente e quando l'elemento diventa attivo viene impostata la variabile.

http://plnkr.co/edit/CvPCVxy4MfJEM1UksrrA?p=preview

abbiamo separato oggi con successo l'attenzione dal codice di controllo. Si occupa anche di tutti i problemi di timeout $ (credo). L'unica cosa di cui devi essere consapevole è che non dovresti usare la stessa variabile per legare al fuoco due elementi diversi.

MODIFICA: Aggiornato il plunk poiché il precedente non funzionava correttamente.

+0

Questo è tutto in una direttiva. – anonymous

+0

@eyston questo è il punto. Tutte le tue cose DOM ora sono nella direttiva. Inoltre, si noti che la direttiva non contiene alcuna logica specifica dell'applicazione. Questa è la corretta separazione della logica. Vedi questo thread: https://github.com/angular/angular.js/issues/1277 – ganaraj

+1

Sto dicendo che tutto il codice nella mia domanda è presente in una direttiva. – anonymous