I have been working a lot with AngularJS lately, and wanted to share my experience integrating the jQuery isotope plugin with Angular.
This post is spurred my the discussion on the mailing list
In the app I’m currently working on I make isotope and angular work together ultimately by using a technique like this
Basically, I instantiate isotope outside of angular after document.ready in normal jQuery fashion. Inside angular land I use ng-repeat to build my DOM. Since angular builds up the DOM before the document.ready event fires, this works just fine. The only issue is that when I add things to the DOM, I have to tell isotope to update itself.
I do this by using $broadcast to trigger an event in the directive that calls the correct isotope function that updates the DOM in isotope fashion.
The key point to notice here is that when I call the isotope function to update the DOM I have to wrap it in $timeout with no delay. Why do we have to do this? Whats so magical about $timeout?
Well, most of the magic that makes two way bindings work in angular happen asynchronously. So what is happening here is that when I want isotope to update the DOM, there is no new DOM elements for it to update yet.
This is the basic flow of events without $timeout
- Angular kicks off its code to update the DOM asynchronously.
- Isotope DOM update code runs but Angular hasn’t updated the DOM yet.
Here is the basic flow of events with $timeout
- Angular kicks off its code to update the DOM async.
- Call to isotope DOM update is wrapped in timeout, so its moves to bottom of the async stack
- Angular DOM update completes because its next in the async stack.
- Isotope DOM update completes with the correct DOM nodes because its last in the async stack (usually, if no other
$timeout’s was called)
So basically when you wrap a function in $timeout you push that call to the bottom of the browsers async stack, so in this case the isotope call will be called (hopefully) when angular is done updating the DOM.
This is because in javascript nothing really happens at the same time, there is only one thread, and by calling timeout you can push things to the bottom of that thread. For more on this see here and here.
Ultimately though, I usually don’t like to call $timeout because it feels somewhat hackish, and you can get into trouble if you abuse it and call it in another callback function and forget that code was already moved down the stack.
Sometimes you might figure out a way to not use $timeout, as Max did by just not using ng-repeat like this
In the end I found this solution to be the best for my tastes.
It still uses $timeout but it doesn’t call isotope outside of angular, it keeps everything inside angular which is nice.
Hopefully this was a good explanation of when you might want to use $timeout in places you wouldn’t normally.