175

I have a function which I want to call after page content is loaded. I read about $viewContentLoaded and it doesn't work for me. I am looking for something like

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

Above call doesn't work for me in AngularJs controller.

1

15 Answers 15

167

According to documentation of $viewContentLoaded, it supposed to work

Emitted every time the ngView content is reloaded.

$viewContentLoaded event is emitted that means to receive this event you need a parent controller like

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

From MainCtrl you can listen the event

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

Check the Demo

Sign up to request clarification or add additional context in comments.

9 Comments

The problem with this approach is that controllers have not fully initialized at this point.
@Reza He is correct, this event is fired when the dom is added but before angular has finished processing the dom. You can test this by adding a id or class as angular variable and try to find it in $viewContentLoaded with jQuery. You will not find it.
@Reza yes,Thomas is right the form element cannot access inside the $viewContentLoaded say,$scope.formName.elementName.$setValidity("validateRule", false); will throw "TypeError: Cannot read property 'elementName' of undefined"
The same can be called on $rootScope level. Instead of $scope.$on, it should be $rootScope.$on and inside of app.run() block
This option will not work when you have ng-repeat and several nested directives that will generate HTML/Content based on complex loops and conditions. Rendering continues for some time, even after $viewContentLoaded event is triggered. How you can ensure that all elements have been fully rendered then execute certain code? Any feedback?
|
117

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

5 Comments

Does this go within the app.js ?
you can listen to it in your controllers.js
Works ok in main app.js file too.
That answer works for me! Just see that on documentation element page ' ready() (deprecated, use angular.element(callback) instead of angular.element(document).ready(callback)) '
This worked for me. The accepted answer approach does not work inside the controller.
75

fixed - 2015.06.09

Use a directive and the angular element ready method like so:

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>

or for those using controller-as syntax...

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

The benefit of this is that you can be as broad or granular w/ your UI as you like and you are removing DOM logic from your controllers. I would argue this is the recommended Angular way.

You may need to prioritize this directive in case you have other directives operating on the same node.

10 Comments

this looks like a really good solution, but I couldn't make it work. it choked at elem.ready( $scope.$apply( readyFunc( $scope ))); Any ideas why that might be? Perhaps an update to angular happened? Anyway - it's an elegant approach, if it could work.
nm, I see what the issue was. There was a typo on the attrs.onReady, should be what it is now. The other issue was that I was calling it funky.... ... what I get for converting coffeescript from memory to JS.
I think in certain cases that may be fine, but my arguments against it are: I think the expectations of his code when just glancing is that that would return some text value to display in the DOM,ergo it requires a careful eye; It doesn't allow any timing in terms of directive priority, you are stuck at the controller level unless you use $timeout; You're explicitly creating an addt. DOM node simply to execute non-dom logic; It's only reusable if that method resides far up in the $scope hierarchy such that child scopes inherit it;I just think it looks bad in an NG perspective;
Worked for me after adding a $timeout(function() {}) around the elem.ready(). Otherwise it was throwing a $digest already in progress error. But anyway, thanks a ton! I have been struggling with this for the past hour.
Digging through the $scope appears to be empty. Any ideas?
|
70

You can directly call it by adding {{YourFunction()}} after HTML element.

Here is a Plunker Link.

13 Comments

Could you please elaborate more your answer adding a little more description about the solution you provide?
When calling a function like this. Make sure its invoked once or otherwise the digest cycle will run infinite and shows an error
i like this solution, it is realy so simple
This is fantastic - only one problem. It appears that my function runs 5 times when I use this. I added a counter so it's working fine but I'm just wondering if anyone knows why that would occur... Seems a bit strange you know?
This causes infinite loops and should not be used. Use <div ng-init="yourFuction()"></div> as shown in Saad's answer
|
25

I had to implement this logic while handling with google charts. what i did was that at the end of my html inside controller definition i added.

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

and in that function simply call your logic.

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

4 Comments

I was using this but at the top of the page, the init required some div to be loaded already so by moving the ng-init to the bottom of the page fixed my issue, nice tip and it makes sense.
only answer that helped me with my problem (jquery mobile empty selection)
This is the real answer as it adapts to dynamically loaded controls and is super easy to implement.
this answer saved me some time, so elegant and effective for my use case
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
    };

}] );

Above method worked for me

Comments

3

you can call javascript version of onload event in angular js. this ng-load event can be applied to any dom element like div, span, body, iframe, img etc. following is the link to add ng-load in your existing project.

download ng-load for angular js

Following is example for iframe, once it is loaded testCallbackFunction will be called in controller

EXAMPLE

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>

2 Comments

Please add some example code to show how the OP can solve the problem
@jro Updated my ans. Hope it will be helpful :)
2

If you're getting a $digest already in progress error, this might help:

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

            })
        }
    }

Comments

2

I was using {{myFunction()}} in the template but then found another way here using $timeout inside the controller. Thought I'd share it, works great for me.

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

}]);

2 Comments

I tried all the answers here and for some reason, $timeout is the only thing that worked for me. Not sure why, but it could have something to do with ng-include and the timing of initiating services.
I added a plus one for {{myFunction()}}
1

Running after the page load should partially be satisfied by setting an event listener to the window load event

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

Inside the module.run(function()...) of angular you will have all access to the module structure and dependencies.

You can broadcast and emit events for communications bridges.

For example:

  • module set onload event and build logic
  • module broadcast event to controllers when logic required it
  • controllers will listen and execute their own logic based on module onload processes.

Comments

1

I found that if you have nested views - $viewContentLoaded gets triggered for every of the nested views. I've created this workaround to find the final $viewContentLoaded. Seems to work alright for setting $window.prerenderReady as required by Prerender (goes into .run() in the main app.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 ++;
});

Comments

0

If you want certain element to completely loaded, Use ng-init on that element .

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

the initModalFacultyInfo() function should exist in the controller.

Comments

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

1 Comment

While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value.
0

The solution that work for me is the following

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

Controller code is the following

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

Html code is the following

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

Comments

-31

I use setInterval to wait for the content loaded. I hope this can help you to solve that 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);

2 Comments

Is people still recommend setInterval as a solution in 2016 ?
but there is no simple api in agular... how .. how to do it?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.