Creating An Optimized Switch Directive For Use With ngRepeat In AngularJS
Yesterday, I looked at how to use a single ngRepeat directive, in AngularJS, to render a list of commingled datasets. In that approach, I used the ngSwitch and ngSwitchWhen directives. While this worked perfectly well, I was concerned that it was causing much of the DOM (Document Object Model) to be compiled twice - once for the ngRepeat and once for each ngSwitchWhen. As such, I wanted to take a stab at creating a "switch" directive that was optimized to work specifically with ngRepeat and commingled data.
My concern with the vanilla ngSwitch / ngSwitchWhen approach is that ngSwitchWhen uses "transclude:element" in its directive configuration. I believe (though I am not 100% sure) that this means that each ngSwitchWhen directive will be re-compiled every time the parent ngRepeat element is cloned and linked. To me, this seems unnecessary for the given context since we already know that the ngRepeat element is going to be compiled.
To get around this, what I'd like is precompile the case/when templates before the ngRepeat directive runs. Then, as each ngRepeat clone is being added to the DOM, we clone and link the appropriate rendering template. The real complexity here is that we need access to two different phases of the directive lifecycle - before ngRepeat and after ngRepeat.
I don't believe that this behavior can be achieved in a single directive. So, what I've done in the following code is to do the precompiling in a pre-ngRepeat directive - bnRepeatSwitch; but, then to have that directive inject a different, post-ngRepeat directive - bnRepeatSwitchOn - which will take care of the per-item linking and template selection.
There's definitely more code here since we have to define new directives; but, if you look at the HTML, there's hardly any difference at all. So, is the added complexity worthwhile? It's hard to say exactly, especially with isolated testing. But, when I look at Chrome Dev Tools, I do see significantly faster rendering times and significantly lower memory usage.
NOTE: I demonstrate the performance and memory benefits of this approach in the video.
This approach isn't quite as flexible as the ngSwitch/ngSwitchWhen approach since my directive clears out the entire ngRepeat content before rendering the selected template. But, that's more a factor of laziness in my implementation rather than a limitation of the approach itself. And, in my experience, these kinds of optimizations do add up and lead to a faster, more responsive user experience as the size of your AngularJS application grows.
Want to use code from this post? Check out the license.
its great article, thanks.
but its working with one ng-repeat,
does compile function not run again for second ng-repeat? how can i fix it.
actually, i wrote a little bit wrong :(
when using bnRepeatSwitch on ng-repeat that is in another ng-repeat, switchCases property can not read...
<ul ng-repeat="people in peopleArray">
<li ng-repeat="person in people"
I don't _think_ it should matter since the inner ngRepeat element will be compiled and linked when the inner ngRepeat directive is compiled and linked. I'll have to try this out on my end to see if I can figure out why it wouldn't be working.
Hmm, you are right. If you use this inside another ngRepeat, it will work for the first ngRepeat and then break on the second ngRepeat. I think it has to do with when things are getting compiled. Having two nested things with "terminal" compilation is hurting my head :D I will have to sit and think on this a bit more. Hopefully will have a better answer tomorrow.
After some quick tinkering I believe it has to do with the fact that my directive is actually being compiled once (for the outer ngRepeat transclusion), but then linked twice (one for each outer ngRepeat item). I will have to sit and tinker before I can figure out the best approach.
Thanks for bringing this to my attention!!
I think I figured it out. The directive gets compiled once. Then, a new controller is created for each linking phase. I'll have something working later tonight.
Thanks again for your help in pointing out this problem. I have updated the code to fix the nested transclusion error. I've also created a "test" page that demonstrates the usage inside a nested ng-repeat:
I hope that helps.
ur articles are rock... thank for sharing...
I am "just starting out" with AngularJS --- I always have liked your way of expression/learning , so I am curious if you can direct me down the right path to learning this technology - The right way, from the get go and something that doesn't go too slow. Any advice?
Hi! Great article.
It really helped me solve a issue I was having.
Thanks a lot man!