Jak uruchomić funkcję w kontrolerze AngularJS na dokumencie gotowym?

254

Mam funkcję w moim kontrolerze kątowym, chciałbym, aby ta funkcja była uruchamiana na dokumencie gotowym, ale zauważyłem, że funkcja kątowa uruchamia ją podczas tworzenia domeny.

 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
 }

Czy ktoś wie, jak sobie z tym poradzić?

foreyez
źródło

Odpowiedzi:

449

Możemy użyć tej angular.element(document).ready()metody, aby dołączyć wywołania zwrotne, gdy dokument jest gotowy. Możemy po prostu dołączyć wywołanie zwrotne w kontrolerze w następujący sposób:

angular.module('MyApp', [])

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

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

Będzie
źródło
64
lub możesz użyć $ document.ready (function () {...}), Angular Docs: docs.angularjs.org/api/ng/service/$document
StuR
29
Musisz $documentużyć zastrzyku, aby go użyć. angular.elementdziała od razu po wyjęciu z pudełka.
nshew
17
Będziesz musiał wstrzyknąć dokument $, aby go użyć, ale jest to zalecany sposób odwoływania się do elementu dokumentu. Nie musisz, ale ułatwia to testowanie.
Jason Cox
10
documentjest zmienną globalną, w której $documentmożna ją wstrzykiwać pod kątem, co ułatwia testowanie. Zasadniczo trudno jest testować aplikacje, które uzyskują dostęp do globalnych zmiennych JS, takich jak dokument lub okno. Myślę też, że module.runto dobre miejsce na umieszczenie tego kodu zamiast kontrolera.
lanoxx,
7
Dla mnie to nie działa, wywołanie getElementById zwraca null.
ThePartyTurtle,
29

Zobacz ten post Jak wykonać funkcję kontrolera kątowego przy ładowaniu strony?
Do szybkiego wyszukiwania:

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

W ten sposób nie musisz czekać, aż dokument będzie gotowy.

vinesh
źródło
1
co jeśli muszę zadzwonić po wczytaniu strony?
Rishi
22

Angular inicjuje się automatycznie po DOMContentLoadedzdarzeniu lub po ocenie skryptu angular.js, jeśli w tym momencie document.readyState jest ustawiony na „complete”. W tym momencie Angular szuka dyrektywy ng-app, która określa katalog główny aplikacji.

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

Oznacza to, że kod kontrolera będzie działał, gdy DOM będzie gotowy.

Więc to jest po prostu $scope.init().

Will M.
źródło
9
Próbowałem to zrobić, ale dziwnie to nie działa kilka rzeczy w mojej stronie nie zostały załadowane
foreyez
i jak wiedzieć, kiedy kod kątowy jest gotowy przed wykonaniem czynności takich jak klikanie przycisków itp. podczas pisania automatycznych testów internetowych?
akostadinov
DOMContentLoadedJest również uruchamiany, gdy dokument został całkowicie załadowany i przeanalizowany, bez oczekiwania na zakończenie ładowania arkuszy stylów, obrazów i podramek. Aby uzyskać więcej informacji, zobacz Jaka jest różnica między zdarzeniem DOMContentLoaded a ładowaniem? .
georgeawg
22

Angular ma kilka punktów czasowych, aby rozpocząć wykonywanie funkcji. Jeśli szukasz czegoś takiego jak jQuery

$(document).ready();

Ten analog może być bardzo przydatny:

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

Ten jest pomocny, gdy chcesz manipulować elementami DOM. Zacznie działać dopiero po załadowaniu wszystkich elementów.

UPD: To, co powiedziano powyżej, działa, gdy chcesz zmienić właściwości css. Czasami jednak nie działa, gdy chcesz zmierzyć właściwości elementu, takie jak szerokość, wysokość itp. W takim przypadku możesz spróbować:

$scope.$watch('$viewContentLoaded', 
    function() { 
        $timeout(function() {
            //do something
        },0);    
});
Alex Link
źródło
To zasługuje na więcej niż sugerowana odpowiedź. Dostałem tę pracę bez użycia $ timeout.
Richie86,
działa dla mnie tylko z setTimeout (0) - bez zawartości na przykład powtórzenie ng nie jest jeszcze ładowane w tym momencie
Ignacio Vazquez
2

Miałem podobną sytuację, w której musiałem wykonać funkcję kontrolera po załadowaniu widoku, a także po załadowaniu, zainicjowaniu określonego komponentu innej firmy w widoku i umieszczeniu odwołania do siebie na $ scope. To, co skończyło się dla mnie działaniem, to ustawienie zegarka na tej właściwości zakresu i uruchamianie mojej funkcji dopiero po jej zainicjowaniu.

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

Obserwator jest wywoływany kilka razy z nieokreślonymi wartościami. Następnie, gdy siatka jest tworzona i ma oczekiwaną właściwość, można bezpiecznie wywołać funkcję inicjalizacji. Przy pierwszym wywołaniu obserwatora z nieokreśloną newValue, oldValue nadal będzie niezdefiniowana.

MikeyM
źródło
1

Oto moja próba użycia zewnętrznego kontrolera za pomocą coffeescript. Działa raczej dobrze. Pamiętaj, że settings.screen.xs | sm | md | lg są wartościami statycznymi zdefiniowanymi w nieglifikowanym pliku, który dołączam do aplikacji. Wartości są zgodne z oficjalnymi punktami przerwania Bootstrap 3 dla tytułowych rozmiarów zapytań o media:

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
źródło
1

Odpowiedź

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

jest jedynym, który działa w większości testowanych przeze mnie scenariuszy. Na przykładowej stronie z 4 komponentami, z których wszystkie tworzą HTML z szablonu, kolejność zdarzeń była następująca

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

Zatem $ document.ready () jest w większości przypadków bezużyteczny, ponieważ DOM konstruowany pod kątem może nie być prawie gotowy.

Ale co ciekawsze, nawet po uruchomieniu $ viewContentLoaded nie można było znaleźć elementu zainteresowania.

Dopiero po wykonaniu limitu czasu $ został znaleziony. Zauważ, że chociaż wartość limitu czasu $ była równa 0, upłynęło prawie 200 milisekund, zanim zostało wykonane, co wskazuje, że ten wątek został wstrzymany na dłuższą chwilę, prawdopodobnie gdy DOM miał szablony kątowe dodane do głównego wątku. Całkowity czas od pierwszego $ document.ready () do ostatniego $ timeout wyniósł prawie 500 milisekund.

W jednym wyjątkowym przypadku, w którym ustawiono wartość komponentu, a następnie wartość text () zmieniono później w limicie czasu $, wartość limitu czasu $ musiała być zwiększana aż do zadziałania (nawet jeśli element można było znaleźć podczas limitu czasu $ ). Coś asynchronicznego w komponencie innej firmy spowodowało, że wartość miała pierwszeństwo przed tekstem, dopóki nie minęło wystarczająco dużo czasu. Inną możliwością jest $ scope. $ EvalAsync, ale nie został wypróbowany.

Nadal szukam tego jednego wydarzenia, które mówi mi, że DOM całkowicie się ustabilizował i można nim manipulować, aby wszystkie przypadki działały. Jak dotąd konieczna jest dowolna wartość limitu czasu, co oznacza, że ​​w najlepszym wypadku jest to kludge, która może nie działać w wolnej przeglądarce. Nie próbowałem opcji JQuery, takich jak liveQuery i publikować / subskrybować, które mogą działać, ale z pewnością nie są czysto kątowe.

Wray Smallwood
źródło
1

Jeśli dostajesz coś podobnego getElementById call returns null, to prawdopodobnie dlatego, że funkcja jest uruchomiona, ale identyfikator nie miał czasu na załadowanie do DOM.

Spróbuj użyć odpowiedzi Willa (u góry) z opóźnieniem. Przykład:

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
        });
    });
}]);
Pan Zielony
źródło
0

Dlaczego nie spróbować z tym, co wspominają dokumenty kątowe https://docs.angularjs.org/api/ng/function/angular.element .

angular.element (callback)

Użyłem tego w mojej funkcji $ onInit () {...}.

 var self = this;

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

To zadziałało dla mnie.

Mahib
źródło
0
$scope.$on('$ViewData', function(event) {
//Your code.
});
Shreyansh Kumar
źródło