Monday, September 9, 2019

Controller expression evaluation on render with Angular

This is a really common need that AngularJS is not providing out of the box.
I needed it myself for some projects and you might too?
Ok, even if many will tell you that you should not 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.
So here we go.
What I'm using to get expressions evaluated after the render is a custom directive named, you guessed, afterRender:
define(['angular'], function (angular) {
  'use strict';
  return angular.module('app.common.after-render', [])
    .directive('afterRender', [ function() {
    var def = {
        restrict : 'A', 
        terminal : true,
        transclude : false,
        link : function(scope, element, attrs) {
            if (attrs) { scope.$eval(attrs.afterRender) }
            scope.$emit('onAfterRender')
        }
    };
    return def;
    }]);
});
Transclusion doesn't make sense here, so we set it false and is restricted to 'A' because, to be useful, what we want is to add this into any controller's template by adding it as an attribute like this:
which will make the controller owning that piece of template able to observe the event onAfterRender fired by this directive and react with arbitrary code of its own.
Here is an example of such controller's initialize method would look like:
$scope.initialize = function () {
    // Makes this controller to have some default initial state and 
    // wires the reactions that belong to its concern.

    $scope.$on('onAfterRender', function (){ $scope.thatGuyJustRendered()});

};

// Reacts doing this and that after that guy had rendered
$scope.thatGuyJustRendered = function () {
    console.log('yep, cool, this computes after that guy had rendered');
};

// ... other controller's methods ...

$scope.initialize();
Bonus There is a bonus. But! this is powerful so it comes with a disclaimenr: this is something not to be abused. I do not endorse bloating the views (templates) with logic because that's something that concerns the controller.
Said that, here is how you'd use it to run an arbitrary expression:
You can put there where you see the $emit any expression you want but you should know that in all my real-world code using the after-render, 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.
I hope you find it useful and let me know if you need help with it or have any remarks about it

No comments:

Post a Comment