0

Why is $timeout running before directive render. Here is my directive

angular.module('directiveTestModule').directive('popupMessage', ['$filter', 'crudService', 'serviceConfiguration', '$rootScope', '$timeout','$parse',
    function ($filter, crudService, serviceConfiguration, $rootScope, $timeout, $parse) {
        return {
            restrict: 'E',
            replace: true,
            scope: { instance:'=instance',  options: '=options' },
            replace: true,
            templateUrl: 'Scripts/app/shared/popupMessage/popupMessageView.html',
            controller: function ($scope, $element) {

            },
            link: function ($scope, $element, attr) {
                //$scope.instance = $parse(attr['popupMessage'])($scope) || {};
                $scope.instance = {};
                $scope.instance.showMessage = function () {
                    alert($scope.options.name)
                }
            }
        }
    }]);

And here is the code on my controller

$timeout(function () {
                $scope.instancePopUp.showMessage()
            }, 0)

And this is the html code

<popup-message instance="instancePopUp" options="optionsPopup"></popup-message>

Sometimes, the timeout function runs before directive's link function. As long as I know timeout should be run after all directives initializations, am I wrong? Is there a way to make sure directive runs before $timeout on controller?

Flezcano
  • 1,647
  • 5
  • 22
  • 39
  • You have asynchronously loaded template, and link is postponed until it can be linked. Of course, this will take more than *0 ms* on first directive compilation. – Estus Flask Apr 10 '18 at 15:27
  • 1
    Use of `$timeout` is a [code smell](https://en.wikipedia.org/wiki/Code_smell), a symptom of a bigger problem. In directives, controllers always load before the link functions. Consider using the `$postLink()` [Life-cycle hook](https://docs.angularjs.org/api/ng/service/$compile#life-cycle-hooks) which is guaranteed to be called after this controller's element and its children have been linked. – georgeawg Apr 10 '18 at 19:36

2 Answers2

0

The issue is in the first load of your directive, the template is not loaded and it takes some times to load it and there is no guarantee your timeout runs after it.

You can pre-load your directive template in the module run

angular.module('directiveTestModule').run(function ($templateCache, $http) {
        $http.get('Scripts/app/shared/popupMessage/popupMessageView.html', { cache: $templateCache });
});

This pre-load your directive template which means your link function runs after the controller without waiting.

Another solution is to move your timeout logic from controller to link function.

Arashsoft
  • 2,749
  • 5
  • 35
  • 58
0

In directives, controllers always load before the link functions.

Directive functions are loaded in the following order:

compile function
controller
pre-link function
post-link function

For more information, see In which order the directive functions are executed?


Use of $timeout is a code smell, a symptom of a bigger problem.

Consider using the $postLink() Life-cycle hook which is guaranteed to be called after this controller's element and its children have been linked.

From the Docs:

Directive controllers can provide the following methods that are called by AngularJS at points in the life-cycle of the directive:

  • $postLink() - Called after this controller's element and its children have been linked. Similar to the post-link function this hook can be used to set up DOM event handlers and do direct DOM manipulation. Note that child elements that contain templateUrl directives will not have been compiled and linked since they are waiting for their template to load asynchronously and their own compilation and linking has been suspended until that occurs.

— AngularJS Comprehensive Directive API - Life-Cycle Hooks

Community
  • 1
  • 1
georgeawg
  • 48,608
  • 13
  • 72
  • 95