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.

user3233772
źródło
1
Działa to dobrze stackoverflow.com/q/14968690/6521116
Kris Roofe

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

Tasnim Reza
źródło
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');
});
hariszaman
źródło
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.

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

niebieski
źródło
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");
}
Saad Zulfiqar
źródło
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

Alok D
źródło
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>
Darshan Jain
źródło
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);
                }

            })
        }
    }
John Smith
źródło
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.
Luis D. Vazquez Rivera
źródło
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();
});

}]);
Nhan
źródło
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.

NetDeveloper
źródło
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 ++;
});
Maksym
źródło
0
var myTestApp = angular.module("myTestApp", []); 
myTestApp.controller("myTestController", function($scope, $window) {
$window.onload = function() {
 alert("is called on page load.");
};
});
Shreyansh Kumar
źródło
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()">
Jorge Santos Neill
źródło
-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);
xiangry
źródło
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