Zdarzenie AngularJs do wywołania po załadowaniu zawartości


163

Mam funkcję, którą chcę wywołać po załadowaniu zawartości strony. Czytałem o $ viewContentLoaded i to nie działa dla mnie. Szukam czegoś takiego

document.addEventListener('DOMContentLoaded', function () { 
     //Content goes here 
}, false);

Powyższe wywołanie nie działa dla mnie w kontrolerze AngularJs.


Odpowiedzi:


163

Zgodnie z dokumentacją $ viewContentLoaded , to powinno działać

Emitowane za każdym razem, gdy zawartość ngView jest ponownie ładowana.

$viewContentLoaded jest emitowane zdarzenie, co oznacza, że ​​aby je odebrać, potrzebujesz kontrolera nadrzędnego, takiego jak

<div ng-controller="MainCtrl">
  <div ng-view></div>
</div>

Od MainCtrlmożna posłuchać wydarzenia

  $scope.$on('$viewContentLoaded', function(){
    //Here your view content is fully loaded !!
  });

Sprawdź demo


59
Problem z tym podejściem polega na tym, że na tym etapie kontrolery nie zostały w pełni zainicjowane.
Ash Blue

12
@Reza Ma rację, to zdarzenie jest uruchamiane, gdy dom jest dodawany, ale zanim angular zakończy przetwarzanie domeny. Możesz to sprawdzić, dodając identyfikator lub klasę jako zmienną kątową i spróbować znaleźć ją w $ viewContentLoaded za pomocą jQuery. Nie znajdziesz tego.
Thomas Kekeisen,

1
@Reza tak, Thomas ma rację, element formularza nie może uzyskać dostępu wewnątrz $ viewContentLoaded, powiedzmy, $ scope.formName.elementName. $ SetValidity ("validateRule", false); zgłosi „TypeError: Cannot read property 'elementName' of undefined"
Mussammil

2
To samo można wywołać na poziomie $ rootScope. Zamiast $ scope. $ On, powinno być $ rootScope. $ Na i wewnątrz bloku app.run ()
Vil

4
Ta opcja nie będzie działać, jeśli masz ng-repeati kilka zagnieżdżonych dyrektyw, które będą generować HTML / zawartość na podstawie złożonych pętli i warunków. Renderowanie trwa przez jakiś czas, nawet po $viewContentLoadedwywołaniu zdarzenia. Jak możesz upewnić się, że wszystkie elementy zostały w pełni wyrenderowane, a następnie wykonać określony kod? Jakakolwiek informacja zwrotna?
tarekahf

104

Kątowy <1,6.X.

angular.element(document).ready(function () {
    console.log('page loading completed');
});

Kątowy> = 1.6.X

angular.element(function () {
    console.log('page loading completed');
});

Czy to się dzieje w app.js?
zcoon

3
można go posłuchać w swoim controllers.js
hariszaman

2
Działa również w głównym pliku app.js.
Loubot

Ta odpowiedź mi pasuje! Wystarczy zobaczyć, że na stronie elementu dokumentacji 'ready () (przestarzałe, użyj angular.element (callback) zamiast angular.element (document) .ready (callback))'
Lovera

To zadziałało dla mnie. Przyjęta odpowiedź nie działa w kontrolerze.
Fabiano,

76

naprawiono - 2015.06.09

Użyj dyrektywy i readymetody elementu kątowego w następujący sposób:

js

.directive( 'elemReady', function( $parse ) {
   return {
       restrict: 'A',
       link: function( $scope, elem, attrs ) {    
          elem.ready(function(){
            $scope.$apply(function(){
                var func = $parse(attrs.elemReady);
                func($scope);
            })
          })
       }
    }
})

html

<div elem-ready="someMethod()"></div>

lub dla tych, którzy używają składni kontrolera jako ...

<div elem-ready="vm.someMethod()"></div>

Zaletą tego jest to, że możesz mieć tak szeroki lub szczegółowy interfejs użytkownika, jak chcesz, i usuwasz logikę DOM ze swoich kontrolerów. Twierdzę, że jest to zalecany sposób Angular .

Może być konieczne nadanie priorytetu tej dyrektywie, jeśli masz inne dyrektywy działające w tym samym węźle.


wygląda to na naprawdę dobre rozwiązanie, ale nie udało mi się. zadławił się jakimiś elem.ready( $scope.$apply( readyFunc( $scope ))); pomysłami, dlaczego tak jest? Może nastąpiła aktualizacja do Angulara? Zresztą - to eleganckie podejście, gdyby się udało.
domoarigato

1
nm, widzę, na czym polegał problem. Wystąpiła literówka na temat attrs.onReady, powinno być tym, czym jest teraz. Inną kwestią było to, że nazwałem to funky ... ... co otrzymuję za konwersję coffeescript z pamięci do JS.
jusopi

1
Myślę, że w niektórych przypadkach może to być w porządku, ale moje argumenty przeciwko temu są następujące: myślę, że jego kod, gdy tylko spojrzy, zwróci jakąś wartość tekstową do wyświetlenia w DOM, ergo wymaga to uważnego oka; Nie pozwala na żadne synchronizowanie w kategoriach priorytetu dyrektywy, utkniesz na poziomie kontrolera, chyba że używasz $timeout; Jawnie tworzysz addt. Węzeł DOM po prostu do wykonywania logiki innej niż dom; Można jej użyć ponownie tylko wtedy, gdy ta metoda znajduje się wysoko w hierarchii $ scope, tak że dziedziczą ją zakresy potomne; po prostu myślę, że wygląda źle z perspektywy NG;
jusopi

5
Pracowało dla mnie po dodaniu $timeout(function() {})wokół elem.ready(). W przeciwnym razie generował $digest already in progressbłąd. Ale w każdym razie, wielkie dzięki! Walczę z tym przez ostatnią godzinę.
rkrishnan

1
Przeglądanie $ scope wydaje się być puste. Jakieś pomysły?
MiloTheGreat

66

Możesz to bezpośrednio wywołać, dodając {{YourFunction()}}po elemencie HTML.

Oto plik Plunker Link.


6
Czy mógłbyś bardziej szczegółowo opisać swoją odpowiedź, dodając więcej opisu rozwiązania, które oferujesz?
abarisone

Myślę, że chcesz wywołać funkcję, gdy ten element HTML jest ładowany. Wywołaj więc tę funkcję, jak wspomniano powyżej. Jeśli zgaduję, że masz wątpliwości, to zadziała. :)
niebieski

14
Podczas wywoływania takiej funkcji. Upewnij się, że został wywołany raz lub w inny sposób, cykl podsumowania będzie działał w nieskończoność i wyświetla błąd
Ashan

2
Podoba mi się to rozwiązanie, jest naprawdę takie proste
Kamuran Sönecek

1
To fantastyczne - tylko jeden problem. Wygląda na to, że moja funkcja działa 5 razy, kiedy tego używam. Dodałem licznik, więc działa dobrze, ale zastanawiam się, czy ktoś wie, dlaczego tak się stało ... Wydaje się trochę dziwne, wiesz?
itchyspacesuit

22

Musiałem zaimplementować tę logikę podczas obsługi wykresów Google. co zrobiłem, to że na końcu mojej definicji HTML wewnątrz kontrolera dodałem.

  <body>
         -- some html here -- 
--and at the end or where ever you want --
          <div ng-init="FunCall()"></div>
    </body>

iw tej funkcji po prostu wywołaj swoją logikę.

$scope.FunCall = function () {
        alert("Called");
}

Używałem tego, ale na górze strony init wymagał już załadowania jakiegoś elementu div, więc przeniesienie ng-init na dół strony rozwiązało mój problem, fajna wskazówka i ma sens.
Gurnard

jedyna odpowiedź, która pomogła mi w rozwiązaniu mojego problemu (pusty wybór jQuery mobile)
kaiser

To jest prawdziwa odpowiedź, ponieważ dostosowuje się do dynamicznie ładowanych elementów sterujących i jest bardzo łatwy do wdrożenia.
Jason Sebring

ta odpowiedź zaoszczędziła mi trochę czasu, tak elegancka i skuteczna w moim przypadku
Sello

4
var myM = angular.module('data-module');

myM.directive('myDirect',['$document', function( $document ){

    function link( scope , element , attrs ){

        element.ready( function(){

        } );

        scope.$on( '$viewContentLoaded' , function(){

            console.log(" ===> Called on View Load ") ;

        } );

    }

    return {
        link: link
    };

}] );

Powyższa metoda zadziałała dla mnie


3

możesz wywołać wersję javascript zdarzenia onload w angular js. to zdarzenie ng-load można zastosować do dowolnego elementu dom, takiego jak div, span, body, iframe, img itp. Poniżej znajduje się link do dodania ng-load w istniejącym projekcie.

pobierz ng-load dla angular js

Poniżej znajduje się przykład dla elementu iframe, po załadowaniu testCallbackFunction zostanie wywołany w kontrolerze

PRZYKŁAD

JS

    // include the `ngLoad` module
    var app = angular.module('myApp', ['ngLoad']);
    app.controller('myCtrl', function($scope) {
        $scope.testCallbackFunction = function() {
          //TODO : Things to do once Element is loaded
        };

    });  

HTML

  <div ng-app='myApp' ng-controller='myCtrl'> 
      <iframe src="test.html" ng-load callback="testCallbackFunction()">  
  </div>

Dodaj przykładowy kod, aby pokazać, jak OP może rozwiązać problem
jro

@jro Zaktualizowano moje ans. Mam nadzieję, że okaże się pomocny :)
Darshan Jain

2

Jeśli otrzymujesz błąd podsumowania $ w toku, może to pomóc:

return {
        restrict: 'A',
        link: function( $scope, elem, attrs ) {
            elem.ready(function(){

                if(!$scope.$$phase) {
                    $scope.$apply(function(){
                        var func = $parse(attrs.elemReady);
                        func($scope);
                    })
                }
                else {

                    var func = $parse(attrs.elemReady);
                    func($scope);
                }

            })
        }
    }

0

Uruchomienie po załadowaniu strony powinno być częściowo spełnione przez ustawienie detektora zdarzeń na zdarzenie ładowania okna

window.addEventListener("load",function()...)

W środku module.run(function()...) angulara będziesz mieć pełny dostęp do struktury modułu i zależności.

Możesz broadcastiemit wydarzenia dla mostów komunikacyjnych.

Na przykład:

  • moduł ustawia zdarzenie onload i logikę kompilacji
  • zdarzenie transmisji modułu do kontrolerów, gdy logika tego wymagała
  • kontrolery będą nasłuchiwać i wykonywać swoją własną logikę opartą na procesach ładowania modułów.

0

Używałem {{myFunction()}}w szablonie, ale potem znalazłem inny sposób tutaj, używając $timeoutwewnątrz kontrolera. Pomyślałem, że się tym podzielę, działa świetnie dla mnie.

angular.module('myApp').controller('myCtrl', ['$timeout',
function($timeout) {
var self = this;

self.controllerFunction = function () { alert('controller function');}

$timeout(function () {
    var vanillaFunction = function () { alert('vanilla function'); }();
    self.controllerFunction();
});

}]);

Wypróbowałem wszystkie odpowiedzi tutaj iz jakiegoś powodu $ timeout jest jedyną rzeczą, która działała dla mnie. Nie wiem dlaczego, ale może to mieć coś wspólnego z ng-include i czasem inicjowania usług.
Drellgor

Dodałem plus jeden dla {{myFunction ()}}
commonpike

0

Jeśli chcesz, aby jakiś element został całkowicie załadowany, użyj ng-init na tym elemencie.

na przykład <div class="modal fade" id="modalFacultyInfo" role="dialog" ng-init="initModalFacultyInfo()"> ..</div>

funkcja initModalFacultyInfo () powinna istnieć w kontrolerze.


0

Odkryłem, że jeśli masz zagnieżdżone widoki - $ viewContentLoaded jest wyzwalane dla każdego zagnieżdżonego widoku. Stworzyłem to obejście, aby znaleźć ostateczną wersję $ viewContentLoaded. Wydaje się, że działa dobrze przy ustawianiu $ window.prerenderReady zgodnie z wymaganiami Prerender (przechodzi do .run () w głównej aplikacji.js):

// Trigger $window.prerenderReady once page is stable
// Note that since we have nested views - $viewContentLoaded is fired multiple
// times and we need to go around this problem
var viewContentLoads = 0;
var checkReady = function(previousContentLoads) {
  var currentContentLoads = Number(viewContentLoads) + 0; // Create a local copy of the number of loads
  if (previousContentLoads === currentContentLoads) { // Check if we are in a steady state
    $window.prerenderReady = true; // Raise the flag saying we are ready
  } else {
    if ($window.prerenderReady || currentContentLoads > 20) return; // Runaway check
    $timeout(function() {checkReady(currentContentLoads);}, 100); // Wait 100ms and recheck
  }
};
$rootScope.$on('$stateChangeSuccess', function() {
  checkReady(-1); // Changed the state - ready to listen for end of render
});
$rootScope.$on('$viewContentLoaded', function() {
  viewContentLoads ++;
});

0
var myTestApp = angular.module("myTestApp", []); 
myTestApp.controller("myTestController", function($scope, $window) {
$window.onload = function() {
 alert("is called on page load.");
};
});

Chociaż ten kod może odpowiedzieć na pytanie, zapewnia dodatkowy kontekst dotyczący tego, dlaczego i / lub jak ten kod odpowiada, poprawia jego długoterminową wartość.
rollstuhlfahrer

0

Rozwiązanie, które działa dla mnie, jest następujące

app.directive('onFinishRender', ['$timeout', '$parse', function ($timeout, $parse) {
    return {
        restrict: 'A',
        link: function (scope, element, attr) {
            if (scope.$last === true) {
                $timeout(function () {
                    scope.$emit('ngRepeatFinished');
                    if (!!attr.onFinishRender) {
                        $parse(attr.onFinishRender)(scope);
                    }
                });
            }

            if (!!attr.onStartRender) {
                if (scope.$first === true) {
                    $timeout(function () {
                        scope.$emit('ngRepeatStarted');
                        if (!!attr.onStartRender) {
                            $parse(attr.onStartRender)(scope);
                        }
                    });
                }
            }
        }
    }
}]);

Kod kontrolera jest następujący

$scope.crearTooltip = function () {
     $('[data-toggle="popover"]').popover();
}

Kod HTML jest następujący

<tr ng-repeat="item in $data" on-finish-render="crearTooltip()">

-31

Zwykle setIntervalczekam na załadowanie treści. Mam nadzieję, że pomoże ci to rozwiązać ten problem.

var $audio = $('#audio');
var src = $audio.attr('src');
var a;
a = window.setInterval(function(){
    src = $audio.attr('src');
    if(src != undefined){
        window.clearInterval(a);
        $('audio').mediaelementplayer({
            audioWidth: '100%'
        });
    }
}, 0);

13
Czy ludzie nadal polecają setIntervaljako rozwiązanie w 2016 roku ?
Zachary Dahan

ale w agular nie ma prostego api… jak… jak to zrobić?
Piotr Kula
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.