× {{alert.msg}} Never ask again
Get notified about new tutorials RECEIVE NEW TUTORIALS

How to evaluate an expression after an AngularJS controller has rendered?

Sebastian Sastre
Mar 11, 2015
<p>This is a really common need that AngularJS is not providing out of the box.</p> <p>I needed it myself for some projects and you might too?</p> <p>Ok, even if many will tell you that you <em>should not</em> program in a way that needs to use this feature, I beleive that there is plenty of people that knows exactly what they are doing. There is plenty of cases that have real and justified reasons to use this feature and they can use it properly. In short, when adequate this do not necessarily implies bad design.</p> <p>So here we go.</p> <p>What I'm using to get expressions evaluated after the render is a custom directive named, you guessed, <span style="color:rgb(95, 99, 102)">`afterRender`</span>:</p> <p><code>define(['angular'], function (angular) {<br>   'use strict';<br>   return angular.module('app.common.after-render', [])<br>     .directive('afterRender', [ function() {<br>     var def = {<br>         restrict : 'A', <br>         terminal : true,<br>         transclude : false,<br>         link : function(scope, element, attrs) {<br>             if (attrs) { scope.$eval(attrs.afterRender) }<br>             scope.$emit('onAfterRender')<br>         }<br>     };<br>     return def;<br>     }]);<br> });</code></p> <p><a href="https://docs.angularjs.org/api/ng/directive/ngTransclude">Transclusion</a> doesn't make sense here, so we set it <code>false</code> and is restricted to <code>'A'</code> because, to be useful, what we want is to add this into any controller's template by adding it as an attribute like this:</p> <p><code>&lt;div after-render&gt;&lt;/div&gt;</code></p> <p>which will make the controller owning that piece of template able to observe the event <code><span style="background-color:rgb(247, 247, 249); color:rgb(221, 17, 68)">onAfterRender</span></code> fired by this directive and react with arbitrary code of its own.</p> <p>Here is an example of such controller's <code>initialize</code> method would look like:</p> <p><br> <code>$scope.initialize = function () {<br>     // Makes this controller to have some default initial state and <br>     // wires the reactions that belong to its concern.</code></p> <p><code>    $scope.$on('onAfterRender', function (){ $scope.thatGuyJustRendered()});</code></p> <p><code>};</code></p> <p><code>// Reacts doing this and that after that guy had rendered<br> $scope.thatGuyJustRendered = function () {<br>     console.log('yep, cool, this computes after that guy had rendered');<br> };</code></p> <p><code>// ... other controller's methods ...</code></p> <p><code>$scope.initialize();</code></p> <h2> </h2> <h2>Bonus</h2> <p>There is a bonus. But! this is powerful so it comes with a disclaimenr: this is something <strong><em>not</em> to be abused</strong>. I do not endorse bloating the views <span style="color:rgb(95, 99, 102)">(templates) </span>with logic because that's something that concerns the controller.</p> <p>Said that, here is how you'd use it to run an arbitrary expression:</p> <p><code>&lt;div after-render="$emit='onAfterThisConcreteThingRendered'"&gt;&lt;/div&gt;</code></p> <p><span style="color:rgb(95, 99, 102)">You can put there where you see the <code>$emit</code> any expression you want but you should know that in all my real-world code using the <code>after-render</code>, I only use it to trigger specific events like in that example and program the reaction in the right controller that is wired up with reactions so it knows what to do.</span></p> <p><span style="color:rgb(95, 99, 102)">I hope you find it useful and let me know if you need help with it or have any remarks about it</span></p>
comments powered by Disqus