Come eseguire la funzione nel controller AngularJS sul documento pronto?


254

Ho una funzione nel mio controller angolare, vorrei che questa funzione fosse eseguita sul documento pronto ma ho notato che angolare la esegue mentre viene creata la dom.

 function myController($scope)
 {
     $scope.init = function()
     {
        // I'd like to run this on document ready
     }

     $scope.init(); // doesn't work, loads my init before the page has completely loaded
 }

Qualcuno sa come posso fare per questo?

Risposte:


449

Possiamo usare il angular.element(document).ready()metodo per allegare callback per quando il documento è pronto. Possiamo semplicemente collegare il callback nel controller in questo modo:

angular.module('MyApp', [])

.controller('MyCtrl', [function() {
    angular.element(document).ready(function () {
        document.getElementById('msg').innerHTML = 'Hello';
    });
}]);

http://jsfiddle.net/jgentes/stwyvq38/1/


64
oppure puoi usare $ document.ready (function () {...}), Angular Docs: docs.angularjs.org/api/ng/service/$document
StuR

29
Dovrai iniettare $documentper usarlo. angular.elementfunziona immediatamente.
n.

17
Dovrai iniettare $ document per usarlo, ma è il modo raccomandato per fare riferimento all'elemento del documento. Non è necessario, ma semplifica i test.
Jason Cox,

10
documentè una variabile globale in cui as $documentè iniettabile per via angolare e quindi semplifica i test. In generale è difficile testare le applicazioni che accedono a variabili JS globali come documenti o finestre. Penso anche che module.runsia un buon posto per inserire questo codice al posto del controller.
lanoxx,

7
Questo non funziona per me, la chiamata getElementById restituisce null.
ThePartyTurtle

29

Vedi questo post Come eseguire la funzione del controller angolare al caricamento della pagina?
Per una ricerca veloce:

// register controller in html
<div data-ng-controller="myCtrl" data-ng-init="init()"></div>

// in controller
$scope.init = function () {
    // check if there is query in url
    // and fire search in case its value is not empty
};

In questo modo, non devi aspettare che il documento sia pronto.


1
cosa succede se devo chiamare il caricamento dopo pagina?
Rishi,

22

Angular si inizializza automaticamente DOMContentLoadedall'evento o quando lo script angular.js viene valutato se in quel momento document.readyState è impostato su "complete". A questo punto Angular cerca la direttiva ng-app che designa la radice dell'applicazione.

https://docs.angularjs.org/guide/bootstrap

Ciò significa che il codice del controller verrà eseguito dopo che il DOM è pronto.

Quindi è giusto $scope.init().


9
Ho provato a farlo, ma stranamente non ha funzionato alcune cose nella mia pagina non sono state caricate
foreyez

e come sapere quando il codice angolare è pronto prima di eseguire azioni come fare clic sui pulsanti, ecc. durante la scrittura di test Web aotomati?
Akostadinov,

Viene inoltre DOMContentLoadedattivato quando il documento è stato completamente caricato e analizzato, senza attendere il completamento del caricamento di fogli di stile, immagini e sottotelai. Per ulteriori informazioni, vedere Qual è la differenza tra DOMContentLoaded e caricare gli eventi? .
georgeawg,

22

Angular ha diversi punti temporali per iniziare l'esecuzione delle funzioni. Se cerchi qualcosa come jQuery

$(document).ready();

Potresti trovare questo analogo in angolare molto utile:

$scope.$watch('$viewContentLoaded', function(){
    //do something
});

Questo è utile quando vuoi manipolare gli elementi DOM. Inizierà l'esecuzione solo dopo aver caricato tutti gli elementi te.

UPD: quanto detto sopra funziona quando si desidera modificare le proprietà del CSS. Tuttavia, a volte non funziona quando vuoi misurare le proprietà dell'elemento, come larghezza, altezza, ecc. In questo caso potresti provare questo:

$scope.$watch('$viewContentLoaded', 
    function() { 
        $timeout(function() {
            //do something
        },0);    
});

Questo merita più della risposta suggerita. Ho ottenuto questo lavoro senza usare $ timeout.
Richie86,

funziona per me solo con setTimeout (0) - senza i contenuti sotto un ng-repeat ad esempio non sono ancora caricati a questo punto
Ignacio Vazquez

2

Ho avuto una situazione simile in cui dovevo eseguire una funzione di controller dopo che la vista veniva caricata e anche dopo che un particolare componente di terze parti all'interno della vista veniva caricato, inizializzato e aveva posto un riferimento a se stesso su $ scope. Ciò che alla fine ha funzionato per me è stato impostare un orologio su questa proprietà dell'ambito e attivare la mia funzione solo dopo che era stata inizializzata.

// $scope.myGrid property will be created by the grid itself
// The grid will have a loadedRows property once initialized

$scope.$watch('myGrid', function(newValue, oldValue) {
    if (newValue && newValue.loadedRows && !oldValue) {
        initializeAllTheGridThings();
    }
});

L'osservatore viene chiamato un paio di volte con valori non definiti. Quindi quando la griglia viene creata e ha la proprietà prevista, è possibile chiamare in modo sicuro la funzione di inizializzazione. La prima volta che l'osservatore viene chiamato con un newValue non indefinito, oldValue rimarrà indefinito.


1

Ecco il mio tentativo all'interno di un controller esterno utilizzando coffeescript. Funziona piuttosto bene. Si noti che settings.screen.xs | sm | md | lg sono valori statici definiti in un file non ottimizzato che includo con l'app. I valori si riferiscono ai punti di interruzione ufficiali Bootstrap 3 per le dimensioni dell'omonima media query:

xs = settings.screen.xs // 480
sm = settings.screen.sm // 768
md = settings.screen.md // 992
lg = settings.screen.lg // 1200

doMediaQuery = () ->

    w = angular.element($window).width()

    $scope.xs = w < sm
    $scope.sm = w >= sm and w < md
    $scope.md = w >= md and w < lg
    $scope.lg = w >= lg
    $scope.media =  if $scope.xs 
                        "xs" 
                    else if $scope.sm
                        "sm"
                    else if $scope.md 
                        "md"
                    else 
                        "lg"

$document.ready () -> doMediaQuery()
angular.element($window).bind 'resize', () -> doMediaQuery()

1

La risposta

$scope.$watch('$viewContentLoaded', 
    function() { 
        $timeout(function() {
            //do something
        },0);    
});

è l'unico che funziona nella maggior parte degli scenari che ho testato. In una pagina di esempio con 4 componenti che generano HTML da un modello, l'ordine degli eventi era

$document ready
$onInit
$postLink
(and these 3 were repeated 3 more times in the same order for the other 3 components)
$viewContentLoaded (repeated 3 more times)
$timeout execution (repeated 3 more times)

Quindi un $ document.ready () è inutile nella maggior parte dei casi poiché il DOM in costruzione in angolare potrebbe non essere quasi pronto.

Ma più interessante, anche dopo che $ viewContentLoaded è stato attivato, l'elemento di interesse non è stato ancora trovato.

È stato trovato solo dopo il timeout $ eseguito. Si noti che anche se il timeout $ era un valore di 0, sono trascorsi quasi 200 millisecondi prima che venisse eseguito, indicando che questo thread è stato sospeso per un bel po ', presumibilmente mentre il DOM aveva modelli angolari aggiunti su un thread principale. Il tempo totale dal primo $ document.ready () all'ultima esecuzione $ timeout è stato di quasi 500 millisecondi.

In un caso straordinario in cui è stato impostato il valore di un componente e quindi il valore text () è stato modificato successivamente nel timeout $, il valore $ timeout è stato aumentato fino a quando non ha funzionato (anche se l'elemento è stato trovato durante il timeout $ ). Qualcosa di asincrono all'interno del componente di terze parti ha fatto sì che un valore avesse la precedenza sul testo fino a quando non fosse trascorso un tempo sufficiente. Un'altra possibilità è $ scope. $ EvalAsync, ma non è stato provato.

Sto ancora cercando quell'unico evento che mi dice che il DOM si è completamente stabilizzato e può essere manipolato in modo che tutti i casi funzionino. Finora è necessario un valore di timeout arbitrario, nel migliore dei casi si tratta di un kludge che potrebbe non funzionare su un browser lento. Non ho provato le opzioni di JQuery come liveQuery e pubblicazione / sottoscrizione che potrebbero funzionare, ma certamente non sono pure angolari.


1

Se ricevi qualcosa del genere getElementById call returns null, è probabilmente perché la funzione è in esecuzione, ma l'ID non ha avuto il tempo di caricare nel DOM.

Prova a usare la risposta di Will (verso l'alto) con un ritardo. Esempio:

angular.module('MyApp', [])

.controller('MyCtrl', [function() {
    $scope.sleep = (time) => {
        return new Promise((resolve) => setTimeout(resolve, time));
    };
    angular.element(document).ready(function () {
        $scope.sleep(500).then(() => {        
            //code to run here after the delay
        });
    });
}]);

0

Perché non provare con ciò che i documenti angolari menzionano https://docs.angularjs.org/api/ng/function/angular.element .

angular.element (callback)

L'ho usato nella mia funzione $ onInit () {...}.

 var self = this;

 angular.element(function () {
        var target = document.getElementsByClassName('unitSortingModule');
        target[0].addEventListener("touchstart", self.touchHandler, false);
        ...
    });

Questo ha funzionato per me.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.