Evento de AngularJs para llamar después de cargar contenido


163

Tengo una función a la que quiero llamar después de cargar el contenido de la página. Leí sobre $ viewContentLoaded y no me funciona. Estoy buscando algo como

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

La llamada anterior no funciona para mí en el controlador AngularJs.


Respuestas:


163

De acuerdo con la documentación de $ viewContentLoaded , se supone que funciona

Se emite cada vez que se vuelve a cargar el contenido ngView.

$viewContentLoaded se emite un evento que significa que para recibir este evento necesita un controlador principal como

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

Desde MainCtrltu puedes escuchar el evento

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

Mira la demo


59
El problema con este enfoque es que los controladores no se han inicializado completamente en este momento.
Ash Blue

12
@Reza Tiene razón, este evento se dispara cuando se agrega el dom pero antes de que angular haya terminado de procesar el dom. Puede probar esto agregando un id o clase como variable angular e intente encontrarlo en $ viewContentLoaded con jQuery. No lo encontrarás.
Thomas Kekeisen

1
@Reza sí, Thomas tiene razón, el elemento de formulario no puede acceder dentro de $ viewContentLoaded, digamos, $ scope.formName.elementName. $ SetValidity ("validateRule", false); lanzará "TypeError: No se puede leer la propiedad 'elementName' de undefined"
Mussammil

2
Lo mismo se puede llamar en el nivel $ rootScope. En lugar de $ scope. $ On, debería ser $ rootScope. $ On y dentro del bloque app.run ()
Vil

44
Esta opción no funcionará cuando tenga ng-repeatvarias directivas anidadas que generarán HTML / Contenido en función de bucles y condiciones complejas. La representación continúa durante algún tiempo, incluso después de $viewContentLoadedque se desencadena el evento. ¿Cómo puede asegurarse de que todos los elementos se hayan procesado completamente y luego ejecutar cierto código? ¿Cualquier retroalimentación?
tarekahf

104

Angular <1.6.X

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

Angular> = 1.6.X

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

¿Esto va dentro de la app.js?
zcoon

3
puedes escucharlo en tus controladores.js
hariszaman

2
Funciona bien en el archivo principal app.js también.
Loubot el

¡Esa respuesta me funciona! Solo vea que en la página del elemento de documentación 'ready () (en desuso, use angular.element (callback) en lugar de angular.element (document) .ready (callback))'
Lovera

Esto funcionó para mí. El enfoque de respuesta aceptado no funciona dentro del controlador.
Fabiano

76

fijo - 2015.06.09

Usa una directiva y el elemento angular ready método del manera:

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>

o para aquellos que usan la sintaxis de controlador como ...

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

El beneficio de esto es que puede ser tan amplio o granular con su IU como desee y está eliminando la lógica DOM de sus controladores. Yo diría que este es el angular recomendado forma .

Es posible que deba priorizar esta directiva en caso de que tenga otras directivas que operen en el mismo nodo.


Esto parece una muy buena solución, pero no pude hacerlo funcionar. se ahogó con ¿ elem.ready( $scope.$apply( readyFunc( $scope ))); Alguna idea de por qué podría ser? Tal vez una actualización a angular sucedió? De todos modos, es un enfoque elegante, si pudiera funcionar.
domoarigato

1
nm, veo cuál era el problema. Había un error tipográfico en attrs.onReady, debería ser lo que es ahora. El otro problema era que lo llamaba funky ... ... lo que obtengo por convertir coffeescript de memoria a JS.
jusopi

1
Creo que, en ciertos casos, eso puede estar bien, pero mis argumentos en contra son: creo que las expectativas de su código cuando se echa un vistazo es que eso devolvería algún valor de texto para mostrar en el DOM, por lo tanto, requiere un ojo atento; No permite ningún momento en términos de prioridad directiva, está atascado en el nivel del controlador a menos que lo use $timeout; Estás creando explícitamente un addt. Nodo DOM simplemente para ejecutar lógica no dom; Solo es reutilizable si ese método reside muy arriba en la jerarquía de $ scope, de modo que los ámbitos secundarios lo hereden; solo creo que se ve mal desde una perspectiva NG;
jusopi

55
Trabajó para mí después de agregar un $timeout(function() {})alrededor de la elem.ready(). De lo contrario, arrojaba un $digest already in progresserror. Pero de todos modos, ¡muchas gracias! He estado luchando con esto durante la última hora.
rkrishnan

1
Cavar a través del $ alcance parece estar vacío. ¿Algunas ideas?
MiloTheGreat

66

Puede llamarlo directamente agregando {{YourFunction()}}después del elemento HTML.

Aquí hay un Plunker Link.


66
¿Podría por favor elaborar más su respuesta agregando un poco más de descripción sobre la solución que proporciona?
abarisone

Creo que desea llamar a una función cuando se carga ese elemento html. Entonces llame a esa función como se mencionó anteriormente. Si mi suposición es correcta acerca de su duda, esto funcionará de otra manera, por favor elabore su pregunta. :)
azul

14
Al llamar a una función como esta. Asegúrese de que se invoque una vez o, de lo contrario, el ciclo de resumen se ejecutará infinito y muestra un error
Ashan

2
me gusta esta solución, es realmente muy simple
Kamuran Sönecek

1
Esto es fantástico, solo un problema. Parece que mi función se ejecuta 5 veces cuando uso esto. Agregué un contador para que funcione bien, pero me pregunto si alguien sabe por qué ocurriría eso ... Parece un poco extraño, ¿sabes?
itchyspacesuit

22

Tuve que implementar esta lógica mientras manejaba con google chart. lo que hice fue que al final de mi html dentro de la definición del controlador agregué.

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

y en esa función simplemente llama a tu lógica.

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

Estaba usando esto, pero en la parte superior de la página, el init requería que se cargara un div, así que moviendo el ng-init al final de la página solucionó mi problema, un buen consejo y tiene sentido.
Gurnard

única respuesta que me ayudó con mi problema (selección vacía de jquery mobile)
kaiser

Esta es la respuesta real, ya que se adapta a los controles cargados dinámicamente y es muy fácil de implementar.
Jason Sebring

esta respuesta me ahorró algo de tiempo, tan elegante y efectivo para mi caso de uso
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
    };

}] );

El método anterior me funcionó


3

puede llamar a la versión de JavaScript del evento onload en angular js. Este evento ng-load se puede aplicar a cualquier elemento dom como div, span, body, iframe, img, etc. El siguiente es el enlace para agregar ng-load en su proyecto existente.

descargar ng-load para angular js

El siguiente es un ejemplo para iframe, una vez que se carga testCallbackFunction se llamará en el controlador

EJEMPLO

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>

Agregue un código de ejemplo para mostrar cómo el OP puede resolver el problema
jro

@jro Actualizó mis ans. Espero que sea útil :)
Darshan Jain

2

Si obtiene un error $ digest ya en progreso, esto podría ayudar:

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

La ejecución después de la carga de la página debe satisfacerse parcialmente configurando un detector de eventos para el evento de carga de la ventana

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

Dentro de module.run(function()...) de angular tendrá acceso a la estructura del módulo y sus dependencias.

Puedes broadcastyemit eventos para puentes de comunicaciones.

Por ejemplo:

  • El módulo establece el evento de carga y la lógica de compilación
  • evento de difusión del módulo a los controladores cuando la lógica lo requirió
  • Los controladores escucharán y ejecutarán su propia lógica basada en los procesos de carga del módulo.

0

Estaba usando {{myFunction()}}la plantilla pero luego encontré otra forma aquí usando $timeoutdentro del controlador. Pensé en compartirlo, funciona muy bien para mí.

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

}]);

Intenté todas las respuestas aquí y, por alguna razón, $ timeout es lo único que funcionó para mí. No estoy seguro de por qué, pero podría tener algo que ver con ng-include y el momento de iniciar los servicios.
Drellgor

Agregué uno más para {{myFunction ()}}
commonpike

0

Si desea que cierto elemento se cargue por completo, use ng-init en ese elemento.

p.ej <div class="modal fade" id="modalFacultyInfo" role="dialog" ng-init="initModalFacultyInfo()"> ..</div>

la función initModalFacultyInfo () debería existir en el controlador.


0

Descubrí que si tiene vistas anidadas, $ viewContentLoaded se activa para cada una de las vistas anidadas. He creado esta solución para encontrar el $ viewContentLoaded final. Parece funcionar bien para configurar $ window.prerenderReady como lo requiere Prerender (entra en .run () en la aplicación principal.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.");
};
});

Si bien este código puede responder a la pregunta, proporcionar un contexto adicional con respecto a por qué y / o cómo responde a la pregunta mejora su valor a largo plazo.
rollstuhlfahrer

0

La solución que funciona para mí es la siguiente

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);
                        }
                    });
                }
            }
        }
    }
}]);

El código del controlador es el siguiente

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

El código html es el siguiente

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

-31

Solía setIntervalesperar el contenido cargado. Espero que esto pueda ayudarte a resolver ese problema.

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
¿La gente todavía recomienda setIntervalcomo solución en 2016 ?
Zachary Dahan

pero no hay una API simple en agular ... ¿cómo ... cómo hacerlo?
Piotr Kula
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.