Evento AngularJs para chamar após o conteúdo ser carregado

163

Eu tenho uma função que desejo chamar após o conteúdo da página ser carregado. Eu li sobre $ viewContentLoaded e não funciona para mim. Estou procurando algo como

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

A chamada acima não funciona para mim no controlador AngularJs.

user3233772
fonte
1
Isso funciona bem stackoverflow.com/q/14968690/6521116
Kris Roofe

Respostas:

163

De acordo com a documentação de $ viewContentLoaded , ele deveria funcionar

Emitido sempre que o conteúdo do ngView é recarregado.

$viewContentLoaded evento emitido significa que, para receber esse evento, você precisa de um controlador pai como

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

De MainCtrlvocê pode ouvir o evento

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

Verifique a demonstração

Tasnim Reza
fonte
59
O problema com essa abordagem é que os controladores não foram totalmente inicializados neste momento.
Ash Blue
12
@Reza Ele está correto, este evento é disparado quando o dom é adicionado, mas antes que o angular termine de processar o dom. Você pode testar isso adicionando um ID ou classe como variável angular e tente encontrá-lo em $ viewContentLoaded com jQuery. Você não o encontrará.
Thomas Kekeisen
1
@Reza sim, Thomas está certo, o elemento do formulário não pode acessar dentro de $ viewContentLoaded, por exemplo, $ scope.formName.elementName. $ SetValidity ("validateRule", false); lançará "TypeError: Não é possível ler a propriedade 'elementName' de undefined"
Mussammil 14/10
2
O mesmo pode ser chamado no nível $ rootScope. . Em vez de $ âmbito $ diante, ele deve ser de US $ rootScope $ sobre e dentro de app.run () bloco.
Vil
4
Esta opção não funcionará quando você tiver ng-repeatvárias diretivas aninhadas que gerarão HTML / Conteúdo com base em condições e loops complexos. A renderização continua por algum tempo, mesmo após o $viewContentLoadedevento ser disparado. Como você pode garantir que todos os elementos foram totalmente renderizados e depois executar determinado código? Algum feedback?
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');
});
hariszaman
fonte
Isso acontece no app.js?
Zcoon 31/05
3
você pode ouvi-la em seus controllers.js
hariszaman
2
Também funciona bem no arquivo principal app.js.
Loubot 9/11
Essa resposta funciona para mim! Apenas veja que na página do elemento de documentação 'ready () (obsoleto, use angular.element (callback) em vez de angular.element (document) .ready (callback))'
Lovera
Isso funcionou para mim. A abordagem de resposta aceita não funciona dentro do controlador.
Fabiano
76

fixo - 06.06.2015

Use uma diretiva e o readymétodo do elemento angular da seguinte maneira:

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>

ou para aqueles que usam sintaxe de controlador como ...

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

O benefício disso é que você pode ser tão amplo ou granular com sua interface do usuário quanto desejar e remover a lógica DOM de seus controladores. Eu argumentaria que esta é a maneira angular recomendada .

Pode ser necessário priorizar esta diretiva caso você tenha outras diretivas operando no mesmo nó.

jusopi
fonte
isso parece uma solução muito boa, mas não consegui fazê-lo funcionar. engasgou com elem.ready( $scope.$apply( readyFunc( $scope ))); Alguma idéia de por que isso poderia ser? Talvez tenha ocorrido uma atualização para angular? Enfim - é uma abordagem elegante, se puder funcionar.
domoarigato
1
nm, vejo qual era o problema. Houve um erro de digitação no attrs.onReady, deve ser o que é agora. A outra questão era que eu estava chamando de funky ... ... o que recebo pela conversão de coffeescript da memória para JS.
jusopi
1
Eu acho que em certos casos isso pode ser bom, mas meus argumentos contra são: acho que as expectativas do código dele, ao olhar de relance, são aquelas que retornariam algum valor de texto a ser exibido no DOM; portanto, é necessário um olhar cuidadoso; Ele não permite nenhum tempo em termos de prioridade da diretiva; você fica preso no nível do controlador, a menos que use $timeout; Você está criando explicitamente um addt. Nó DOM simplesmente para executar lógica não-dom; Só é reutilizável se esse método residir muito na hierarquia de escopo $, de modo que os escopos filhos o herdem; só acho que fica ruim na perspectiva do NG;
jusopi
5
Funcionou para mim depois de adicionar um $timeout(function() {})ao redor do elem.ready(). Caso contrário, estava lançando um $digest already in progresserro. Mas enfim, obrigado uma tonelada! Eu tenho lutado com isso durante a última hora.
Rkrishnan
1
A pesquisa no escopo $ parece estar vazia. Alguma ideia?
MiloTheGreat 4/11
66

Você pode chamá-lo diretamente adicionando {{YourFunction()}}após o elemento HTML.

Aqui está um Plunker Link.

azul
fonte
6
Você poderia elaborar mais sua resposta adicionando um pouco mais de descrição sobre a solução que você fornece?
Abarisone 22/05
Eu acho que você deseja chamar uma função quando esse elemento html é carregado. Então chame essa função como mencionado acima. Se meu palpite estiver certo sobre a sua dúvida, isso funcionará caso contrário, elabore sua pergunta. :)
azul
14
Ao chamar uma função como esta. Certifique-se de sua chamada uma vez ou de outra forma a digerir ciclo será executado infinito e mostra um erro
Ashan
2
Eu gosto dessa solução, é realmente muito simples #
Kamuran Sönecek
1
Isso é fantástico - apenas um problema. Parece que minha função é executada 5 vezes quando eu uso isso. Eu adicionei um contador para que ele esteja funcionando bem, mas estou me perguntando se alguém sabe por que isso aconteceria ... Parece um pouco estranho, sabe?
itchyspacesuit
22

Eu tive que implementar essa lógica ao lidar com gráficos do Google. o que eu fiz foi que no final do meu html dentro da definição do controlador eu adicionei.

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

e nessa função simplesmente chame sua lógica.

$scope.FunCall = function () {
        alert("Called");
}
Saad Zulfiqar
fonte
Eu estava usando isso, mas na parte superior da página, o init já exigia que alguma div fosse carregada, movendo o ng-init para a parte inferior da página, corrigi meu problema, boa dica e faz sentido.
Gurnard
única resposta que me ajudou com o meu problema (jquery seleção vazia móvel)
kaiser
Essa é a resposta real, pois se adapta aos controles carregados dinamicamente e é super fácil de implementar.
Jason Sebring
esta resposta salvou-me algum tempo, tão elegante e eficaz para o meu 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
    };

}] );

O método acima funcionou para mim

Alok D
fonte
3

você pode chamar a versão javascript do evento onload em js angulares. esse evento ng-load pode ser aplicado a qualquer elemento dom, como div, span, body, iframe, img etc. A seguir está o link para adicionar o ng-load ao seu projeto existente.

download ng-load para js angulares

A seguir, é apresentado um exemplo para iframe, uma vez carregado, testCallbackFunction será chamado no controlador

EXEMPLO

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
fonte
Por favor, adicione um exemplo de código para mostrar como o OP pode resolver o problema
jro 30/03
@jro Atualizado meu ans. Espero que seja :) útil
Darshan Jain
2

Se você estiver recebendo um erro $ digest já em andamento, isso pode ajudar:

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
fonte
0

A execução após o carregamento da página deve ser parcialmente satisfeita, definindo um ouvinte de evento para o evento de carregamento da janela

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

Dentro module.run(function()...)do angular, você terá todo o acesso à estrutura e dependências do módulo.

Você pode broadcaste emiteventos para pontes de comunicação.

Por exemplo:

  • evento onload do conjunto de módulos e lógica de construção
  • evento de transmissão do módulo para os controladores quando a lógica exigia
  • os controladores ouvirão e executarão sua própria lógica com base nos processos de carga do módulo.
Luis D. Vazquez Rivera
fonte
0

Eu estava usando {{myFunction()}}no modelo, mas depois encontrei outra maneira aqui usando $timeoutdentro do controlador. Pensei que iria compartilhar, funciona muito bem para mim.

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
fonte
Tentei todas as respostas aqui e, por algum motivo, $ timeout é a única coisa que funcionou para mim. Não sei por que, mas pode ter algo a ver com ng-include e o tempo para iniciar os serviços.
Drellgor
Eu adicionei um mais para {{myFunction ()}} #
commonpike
0

Se você deseja que determinado elemento seja completamente carregado, use ng-init nesse elemento.

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

a função initModalFacultyInfo () deve existir no controlador.

NetDeveloper
fonte
0

Descobri que, se você tem visualizações aninhadas - $ viewContentLoaded é acionado para todas as visualizações aninhadas. Eu criei esta solução alternativa para encontrar o $ viewContentLoaded final. Parece funcionar bem para definir $ window.prerenderReady conforme exigido pelo Prerender (entra em .run () no app.js principal):

// 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
fonte
0
var myTestApp = angular.module("myTestApp", []); 
myTestApp.controller("myTestController", function($scope, $window) {
$window.onload = function() {
 alert("is called on page load.");
};
});
Shreyansh Kumar
fonte
Embora esse código possa responder à pergunta, fornecer um contexto adicional a respeito de por que e / ou como esse código responde à pergunta melhora seu valor a longo prazo.
Rollstuhlfahrer
0

A solução que funciona para mim é a seguinte

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

O código do controlador é o seguinte

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

O código HTML é o seguinte

<tr ng-repeat="item in $data" on-finish-render="crearTooltip()">
Jorge Santos Neill
fonte
-31

Eu uso setIntervalpara esperar o conteúdo carregado. Espero que isso possa ajudá-lo a resolver esse 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);
xiangry
fonte
13
As pessoas ainda recomendam setIntervalcomo solução em 2016 ?
Zachary Dahan
mas não há API simples no agular ... como .. como fazê-lo?
Piotr Kula