Wondered what's the best way to detect the finish of page loading/bootstrapping, when all directives done compiling/linking.
Any event already there? Should I overload the bootstrap function?
Wondered what's the best way to detect the finish of page loading/bootstrapping, when all directives done compiling/linking.
Any event already there? Should I overload the bootstrap function?
In the docs for
angular.Module
, there's an entry describing therun
function:So if you have some module that is your app:
You can run stuff after the modules have loaded with:
EDIT: Manual Initialization to the rescue
So it's been pointed out that the
run
doesn't get called when the DOM is ready and linked up. It gets called when the$injector
for the module referenced byng-app
has loaded all its dependencies, which is separate from the DOM compilation step.I took another look at manual initialization, and it seems that this should do the trick.
I've made a fiddle to illustrate.
The HTML is simple:
Note the lack of an
ng-app
. And I have a directive that will do some DOM manipulation, so we can make sure of the order and timing of things.As usual, a module is created:
And here's the directive:
We're going to replace the
test-directive
tag with adiv
of classtest-directive
, and wrap its contents in anh1
.I've added a compile function that returns both pre and post link functions so we can see when these things run.
Here's the rest of the code:
Before we've done anything, there should be no elements with a class of
test-directive
in the DOM, and after we're done there should be 1.It's pretty straightforward. When the document is ready, call
angular.bootstrap
with the root element of your app and an array of module names.In fact, if you attach a
run
function to theapp
module, you'll see it gets run before any of the compiling takes place.If you run the fiddle and watch the console, you'll see the following:
These are all great solutions, However, if you are currently using Routing then I found this solution to be the easiest and least amount of code needed. Using the 'resolve' property to wait for a promise to complete before triggering the route. e.g.
})
Click here for the full docs - Credit to K. Scott Allen
I have come up with a solution that is relatively accurate at evaluating when the angular initialisation is complete.
The directive is:
That can then just be added as an attribute to the
body
element and then listened for using$scope.$on('initialised', fn)
It works by assuming that the application is initialised when there are no more $digest cycles. $watch is called every digest cycle and so a timer is started (setTimeout not $timeout so a new digest cycle is not triggered). If a digest cycle does not occur within the timeout then the application is assumed to have initialised.
It is obviously not as accurate as satchmoruns solution (as it is possible a digest cycle takes longer than the timeout) but my solution doesn't need you to keep track of the modules which makes it that much easier to manage (particularly for larger projects). Anyway, seems to be accurate enough for my requirements. Hope it helps.
i observe DOM manipulation of angular with JQuery and i did set a finish for my app (some sort of predefined and satisfactory situation that i need for my app-abstract) for example i expect my ng-repeater to produce 7 result and there for i will set an observation function with the help of setInterval for this purpose .
Just a hunch: why not look at how the ngCloak directive does it? Clearly the ngCloak directive manages to show content after things have loaded. I bet looking at ngCloak will lead to the exact answer...
EDIT 1 hour later: Ok, well, I looked at ngCloak and it's really short. What this obviously implies is that the compile function won't get executed until {{template}} expressions have been evaluated (i.e. the template it loaded), thus the nice functionality of the ngCloak directive.
My educated guess would be to just make a directive with the same simplicity of ngCloak, then in your compile function do whatever you want to do. :) Place the directive on the root element of your app. You can call the directive something like myOnload and use it as an attribute my-onload. The compile function will execute once the template has been compiled (expressions evaluated and sub-templates loaded).
EDIT, 23 hours later: Ok, so I did some research, and I also asked my own question. The question I asked was indirectly related to this question, but it coincidentally lead me to the answer that solves this question.
The answer is that you can create a simple directive and put your code in the directive's link function, which (for most use cases, explained below) will run when your element is ready/loaded. Based on Josh's description of the order in which compile and link functions are executed,
From this we can conclude that we can simply make a directive to execute our code when everything is ready/compiled/linked/loaded:
Now what you can do is put the ngElementReady directive onto the root element of the app, and the
console.log
will fire when it's loaded:It's that simple! Just make a simple directive and use it. ;)
You can further customize it so it can execute an expression (i.e. a function) by adding
$scope.$eval($attributes.ngElementReady);
to it:Then you can use it on any element:
Just make sure you have your functions (e.g. bodyIsReady and divIsReady) defined in the scope (in the controller) that your element lives under.
Caveats: I said this will work for most cases. Be careful when using certain directives like ngRepeat and ngIf. They create their own scope, and your directive may not fire. For example if you put our new ngElementReady directive on an element that also has ngIf, and the condition of the ngIf evaluates to false, then our ngElementReady directive won't get loaded. Or, for example, if you put our new ngElementReady directive on an element that also has a ngInclude directive, our directive won't be loaded if the template for the ngInclude does not exist. You can get around some of these problems by making sure you nest the directives instead of putting them all on the same element. For example, by doing this:
instead of this:
The ngElementReady directive will be compiled in the latter example, but it's link function will not be executed. Note: directives are always compiled, but their link functions are not always executed depending on certain scenarios like the above.
EDIT, a few minutes later:
Oh, and to fully answer the question, you can now
$emit
or$broadcast
your event from the expression or function that is executed in theng-element-ready
attribute. :) E.g.:EDIT, even more few minutes later:
@satchmorun's answer works too, but only for the initial load. Here's a very useful SO question that describes the order things are executed including link functions,
app.run
, and others. So, depending on your use case,app.run
might be good, but not for specific elements, in which case link functions are better.EDIT, five months later, Oct 17 at 8:11 PST:
This doesn't work with partials that are loaded asynchronously. You'll need to add bookkeeping into your partials (e.g. one way is to make each partial keep track of when its content is done loading then emit an event so the parent scope can count how many partials have loaded and finally do what it needs to do after all partials are loaded).
EDIT, Oct 23 at 10:52pm PST:
I made a simple directive for firing some code when an image is loaded:
EDIT, Oct 24 at 12:48am PST:
I improved my original
ngElementReady
directive and renamed it towhenReady
.For example, use it like this to fire
someFunction
when an element is loaded and{{placeholders}}
not yet replaced:someFunction
will be called before all theitem.property
placeholders are replaced.Evaluate as many expressions as you want, and make the last expression
true
to wait for{{placeholders}}
to be evaluated like this:someFunction
andanotherFunction
will be fired after{{placeholders}}
have been replaced.This only works the first time an element is loaded, not on future changes. It may not work as desired if a
$digest
keeps happening after placeholders have initially been replaced (a $digest can happen up to 10 times until data stops changing). It'll be suitable for a vast majority of use cases.EDIT, Oct 31 at 7:26pm PST:
Alright, this is probably my last and final update. This will probably work for 99.999 of the use cases out there:
Use it like this
Of course, it can probably be optimized, but I'll just leave it at that. requestAnimationFrame is nice.
Angular hasn't provided a way to signal when a page finished loading, maybe because "finished" depends on your application. For example, if you have hierarchical tree of partials, one loading the others. "Finish" would mean that all of them have been loaded. Any framework would have a hard time analyzing your code and understanding that everything is done, or still waited upon. For that, you would have to provide application-specific logic to check and determine that.