Wie führe ich die Funktion im AngularJS-Controller auf Dokument bereit aus?

254

Ich habe eine Funktion in meinem Winkelcontroller. Ich möchte, dass diese Funktion dokumentbereit ausgeführt wird, aber ich habe festgestellt, dass Winkel sie beim Erstellen des Doms ausführt.

 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
 }

Weiß jemand, wie ich das machen kann?

foreyez
quelle

Antworten:

449

Wir können die angular.element(document).ready()Methode verwenden, um Rückrufe anzuhängen, wenn das Dokument fertig ist. Wir können den Rückruf einfach wie folgt in die Steuerung einbinden:

angular.module('MyApp', [])

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

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

Wille
quelle
64
oder Sie können $ document.ready (function () {...}), Angular Docs: docs.angularjs.org/api/ng/service/$document
StuR
29
Sie müssen spritzen $document, um es zu verwenden. angular.elementfunktioniert sofort.
Nshew
17
Sie müssen $ document einfügen, um es verwenden zu können. Es wird jedoch empfohlen, auf das Dokumentelement zu verweisen. Sie müssen nicht, aber es erleichtert das Testen erheblich.
Jason Cox
10
documentist eine globale Variable, bei der as $documentdurch Winkel injizierbar ist und somit das Testen erleichtert. Im Allgemeinen ist es schwierig, Unit-Test-Anwendungen zu erstellen, die auf globale JS-Variablen wie Dokumente oder Fenster zugreifen. Ich denke auch, dass dies module.runein guter Ort ist, um diesen Code anstelle des Controllers einzufügen.
Lanoxx
7
Dies funktioniert bei mir nicht, der Aufruf von getElementById gibt null zurück.
ThePartyTurtle
29

Siehe diesen Beitrag Wie wird die Winkelreglerfunktion beim Laden der Seite ausgeführt?
Für eine schnelle Suche:

// 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
};

Auf diese Weise müssen Sie nicht warten, bis das Dokument fertig ist.

Weinreben
quelle
1
Was ist, wenn ich After-Page-Load aufrufen muss?
Rishi
22

Angular wird automatisch beim DOMContentLoadedEreignis oder beim Auswerten des Skripts angular.js initialisiert , wenn zu diesem Zeitpunkt document.readyState auf 'complete' gesetzt ist. An dieser Stelle sucht Angular nach der ng-app-Direktive, die Ihren Anwendungsstamm festlegt.

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

Dies bedeutet, dass der Controller-Code ausgeführt wird, nachdem das DOM bereit ist.

So ist es einfach $scope.init().

Will M.
quelle
9
Ich habe versucht, das zu tun, aber seltsamerweise hat es nicht funktioniert. Einige Dinge auf meiner Seite wurden nicht geladen
foreyez
und woher wissen Sie, wann der Winkelcode bereit ist, bevor Sie Aktionen wie das Klicken auf Schaltflächen usw. ausführen, wenn Sie automatisierte Webtests schreiben?
Akostadinov
Wird auch DOMContentLoadedausgelöst, wenn das Dokument vollständig geladen und analysiert wurde, ohne darauf zu warten, dass Stylesheets, Bilder und Hilfsrahmen vollständig geladen sind. Weitere Informationen finden Sie unter Was ist der Unterschied zwischen DOMContentLoaded- und Ladeereignissen? .
Georgeawg
22

Angular hat mehrere Zeitpunkte, um Funktionen auszuführen. Wenn Sie nach etwas wie jQuery's suchen

$(document).ready();

Sie können dieses Analogon im Winkel sehr nützlich finden:

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

Dieser ist hilfreich, wenn Sie die DOM-Elemente bearbeiten möchten. Die Ausführung wird erst gestartet, nachdem alle te-Elemente geladen wurden.

UPD: Was oben gesagt wurde, funktioniert, wenn Sie die CSS-Eigenschaften ändern möchten. Manchmal funktioniert es jedoch nicht, wenn Sie die Elementeigenschaften wie Breite, Höhe usw. messen möchten. In diesem Fall möchten Sie möglicherweise Folgendes versuchen:

$scope.$watch('$viewContentLoaded', 
    function() { 
        $timeout(function() {
            //do something
        },0);    
});
Alex Link
quelle
Dies verdient mehr als die vorgeschlagene Antwort. Ich habe diese Arbeit ohne $ timeout bekommen.
Richie86
es funktioniert bei mir nur mit setTimeout (0) - ohne es werden zum Beispiel Inhalte unter einer ng-Wiederholung noch nicht geladen
Ignacio Vazquez
2

Ich hatte eine ähnliche Situation, in der ich eine Controller-Funktion ausführen musste, nachdem die Ansicht geladen wurde und nachdem eine bestimmte Komponente eines Drittanbieters in der Ansicht geladen, initialisiert und einen Verweis auf sich selbst in $ scope platziert hatte. Am Ende funktionierte es für mich, eine Überwachung für diese Scope-Eigenschaft einzurichten und meine Funktion erst nach der Initialisierung auszulösen.

// $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();
    }
});

Der Watcher wird mehrmals mit undefinierten Werten aufgerufen. Wenn das Raster erstellt wird und die erwartete Eigenschaft aufweist, kann die Initialisierungsfunktion sicher aufgerufen werden. Wenn der Watcher zum ersten Mal mit einem nicht undefinierten newValue aufgerufen wird, ist oldValue weiterhin undefiniert.

MikeyM
quelle
1

Hier ist mein Versuch innerhalb eines äußeren Controllers mit Coffeescript. Es funktioniert ziemlich gut. Bitte beachten Sie, dass settings.screen.xs | sm | md | lg statische Werte sind, die in einer nicht hässlichen Datei definiert sind, die ich in die App einbinde. Die Werte beziehen sich auf die offiziellen Bootstrap 3-Haltepunkte für die gleichnamigen Medienabfragegrößen:

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()
akutz
quelle
1

Die Antwort

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

ist die einzige, die in den meisten von mir getesteten Szenarien funktioniert. Auf einer Beispielseite mit 4 Komponenten, die alle HTML aus einer Vorlage erstellen, war die Reihenfolge der Ereignisse

$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)

Daher ist ein $ document.ready () in den meisten Fällen nutzlos, da das im Winkel erstellte DOM möglicherweise noch lange nicht fertig ist.

Interessanter ist jedoch, dass das interessierende Element auch nach dem Auslösen von $ viewContentLoaded immer noch nicht gefunden werden konnte.

Erst nach dem ausgeführten $ timeout wurde es gefunden. Beachten Sie, dass, obwohl das $ timeout einen Wert von 0 hatte, fast 200 Millisekunden verstrichen sind, bevor es ausgeführt wurde, was darauf hinweist, dass dieser Thread eine Weile unterbrochen wurde, vermutlich während dem DOM eckige Vorlagen zu einem Hauptthread hinzugefügt wurden. Die Gesamtzeit von der ersten Ausführung von $ document.ready () bis zur letzten Ausführung von $ timeout betrug fast 500 Millisekunden.

In einem außergewöhnlichen Fall, in dem der Wert einer Komponente festgelegt und der Wert von text () später im $ timeout geändert wurde, musste der $ timeout-Wert erhöht werden, bis er funktionierte (obwohl das Element während des $ timeout gefunden werden konnte ). Durch etwas Asynchrones innerhalb der Komponente eines Drittanbieters hatte ein Wert Vorrang vor dem Text, bis genügend Zeit verstrichen war. Eine andere Möglichkeit ist $ scope. $ EvalAsync, wurde aber nicht ausprobiert.

Ich bin immer noch auf der Suche nach dem einen Ereignis, das mir sagt, dass sich das DOM vollständig beruhigt hat und manipuliert werden kann, damit alle Fälle funktionieren. Bisher ist ein beliebiger Timeout-Wert erforderlich, was bestenfalls bedeutet, dass dies ein Kludge ist, der in einem langsamen Browser möglicherweise nicht funktioniert. Ich habe keine JQuery-Optionen wie liveQuery und Publish / Subscribe ausprobiert, die möglicherweise funktionieren, aber sicherlich nicht rein eckig sind.

Wray Smallwood
quelle
1

Wenn Sie so etwas erhalten getElementById call returns null, liegt dies wahrscheinlich daran, dass die Funktion ausgeführt wird, die ID jedoch keine Zeit zum Laden im DOM hatte.

Versuchen Sie, Wills Antwort (nach oben) mit Verzögerung zu verwenden. Beispiel:

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
        });
    });
}]);
Herr Green
quelle
0

Warum nicht versuchen , mit dem, was Winkel docs erwähnen https://docs.angularjs.org/api/ng/function/angular.element .

angle.element (Rückruf)

Ich habe dies in meiner Funktion $ onInit () {...} verwendet.

 var self = this;

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

Das hat bei mir funktioniert.

Mahib
quelle
0
$scope.$on('$ViewData', function(event) {
//Your code.
});
Shreyansh Kumar
quelle