32

I am building a web application using Angular, and I am trying to find a way to wait until all of the data-ng-include elements have been evaluated and the includes finished loading. For instance the menu is a view that is loaded, as is the main content for each page, so at the very least there are two data-ng-includes that get evaluated and loaded. On top of that the menu include has nested data-ng-repeats that build out my menu. I need a way to initiate script AFTER all of these includes and any angular functions inside them have been loaded and the DOM is ACTUALLY ready.

Is there any event that gets fired when Angular has finished setting up the page?

The implementation of AnguarJS is basic and we are basically using it for templating via use of data-ng-include. Each page is a view (not an angular view, just an html file), loaded via data-ng-include, with the header and footer above and below it. The header also dynamically loads a global view (again not an angular view) html file that includes angular.

So far the menu is the only thing using AngularJS for anything other than templating. It uses it to create a menu using a JSON object that is dynamically generated by JSP and inserted into the DOM that is returned from the server.

In order to make life easier for the front-end guys, any kind of repeated JavaScript functionality is coded out into modules, which are detected on page load and loaded dynamically using a data-features attribute on the element the JavaScript functionality needs to be attached to.

For instance, you may have <div id="mySubMenu" data-features="subMenu"></div>. On page load, each element with a data-features element is detected, and the associated JS module loaded, in this case we would load /scripts/modules/subMenu.js.

What I need is a way to delay running this feature detection and attachment until all of the elements resulting from an include or other angular function are on the page and ready to be manipulated, especially since there may be elements with data-features attributes inside those includes.

Someone elsewhere had mentioned putting a controller on the body and putting nothing side of it but:

$scope.$on('$viewContentLoaded', function() {
    // Init Features Here
});

That did not work, so I am here looking for other options.

I ended up going with Theo's solution in this question: Sending event when angular.js finished loading

13
  • The best answer is NO. You have to change the way you are looking at your app and Model and adapt it to the "angular way". Most likely there is a healthier way to achieve what you need Commented Feb 10, 2014 at 20:26
  • 3
    And what exactly is the "angular way" of attaching traditional JavaScript functionality to page elements after they are loaded (which is basically what I am asking here)? Commented Feb 10, 2014 at 20:38
  • If you need to attach events to elements probably you may want to use a directive but of course I do not have enough information to judge at this stage what is best. Commented Feb 10, 2014 at 20:41
  • Explanation updated and more detail added to aid you in answering the question. Commented Feb 10, 2014 at 20:52
  • You need a directive... Any attempt to go on the opposite direction will delay your from finding the right answer. Commented Feb 10, 2014 at 21:17

6 Answers 6

15

on content loaded:

$rootScope.$on('$includeContentLoaded', function() {
    //do your will
});

on content requested:

$rootScope.$on('$includeContentRequested', function() {
    //...
});
Sign up to request clarification or add additional context in comments.

1 Comment

This doesn't do exactly what I need, but it is a nice bit of kit to have handy. Thank you.
8

I am certain that this is not the correct way to do it but....

Putting the below function in my view controller, ran the function after the view had loaded in my application. I believe that the function is run in the next digest cycle following the view load (correct me here if wrong), so it is run once the page is formed.

setTimeout(function(){
  //do this after view has loaded :)
  console.log('success!');
}, 0);

Its possible you could chain these together via callbacks and/or use an async parallel library to execute another function, after each of the views setTimeouts had returned.

2 Comments

So I should really be using $timeout (the angular wrapper version) instead of setTimeout fyi
Not in this case. The problem here is registering a function BEFORE angular is ready but only running it AFTER angular has done it's initialization. So we can't use $timeout at this point.
3

I think the appropiate answer is NO. You have to change the way you are looking at your app and Model and adapt it to the "angular way". Most likely there is a healthier way to achieve what you need. If you need to attach events to elements probably you may want to use a directive but of course I do not have enough information to judge at this stage what is best.

angular.module along with directives is a good option to explore.

Here is a plunker demo on how to use both with a JQUERY plug in:

http://plnkr.co/edit/WGdNEM?p=preview

Here is a an example with a directive on a separate file:

http://plnkr.co/edit/YNBSWPLeWqsfGvOTwsMB?p=preview

4 Comments

This method still would require me to have a file with all of my directives in it, and load that file on every page load. I need a way to keep each directive in an individual file and load it dynamically as needed.
Each directive can be a separate file, that is actually how I do it during development. Here is an example: plnkr.co/edit/YNBSWPLeWqsfGvOTwsMB?p=preview
Then the last piece of the puzzle is being able to load them dynamically so that they aren't all loaded on every page.
I'm sure this is the way to go forward when building legit Angular applications. I really just needed something to let me hybridize my application between traditional and Angular. Thank you so much for all your help.
3

You can use a JQuery Promise, and fulfill this promise within your angular root controller. Setup the promise before the angular controller or jquery code runs:

var AppReadyDeferred = $.Deferred();
var AppReadyPromise = AppReadyDeferred.promise();

Inside your angular controller once everything is ready, do

AppReadyDeferred.resolve();

And in your jQuery code wait for it to be resolved

window.AppReadyPromise.done(() => {
  // Angular is ready to go!;
});

Comments

1

Use ng-cloak to delay templates being shown before compilation. You can also add a hidden field as the very last child to your html element that has ng-cloak (better to use it in body tag or wherever your ng-app starts) and then you can check for existence of this hidden element in an interval loop

Your hidden field part would be like this

<div ng-include="'ngtplhidden'"></hidden>
<script type="text/ng-template" id="ngtplhidden">
<span id="elemidToCheckInAnIntervalFunc"></span>
</script>

Comments

0

You can try using document.onreadystatechange

document.onreadystatechange = x => {
    console.log('ready!');
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.