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.