Why is the post link function executed before the transcluded child link functions?

你。 提交于 2019-12-31 14:46:14

问题


The timing of (pre/post)link functions in AngularJS are well defined in the documentation

Pre-linking function

Executed before the child elements are linked. Not safe to do DOM transformation since the compiler linking function will fail to locate the correct elements for linking.

Post-linking function

Executed after the child elements are linked. It is safe to do DOM transformation in the post-linking function.

and this blog post clearly illustrates this expected order.

But this order does not seem to apply when using ng-transclude and nested directives.

Here is an example for a dropright element (See the Plunkr)

<!-- index.html -->
<dropright>
  <col1-item name="a">
    <col2-item>1</col2-item>
    <col2-item>2</col2-item>
  </col1-item>
  <col1-item name="b">
    ...
  </col1-item>
</dropright>

// dropright-template.html
<div id="col1-el" ng-transclude></div>
<div id="col2-el">
  <!-- Only angularJS will put elements in there -->
</div>

// col1-item-template.html
<p ng-transclude></p>

// col2-item-template.html
<div ng-transclude></div>

The dropright looks like

The directives write a log in the console when their link and controller functions are called. It usually displays:

But sometimes (after few refreshes), the order is not as expected:

The dropright post-link function is executed before the post-link function of its children.

It may be because, in my particular case, I am calling the dropright controller in the children's directives (See the Plunkr)

angular.module('someApp', [])

.directive('dropright', function() {
    return {
        restrict: 'E',
        transclude: 'true',
        controller: function($scope, $element, $attrs) {
            console.info('controller - dropright');

            $scope.col1Tab = [];
            $scope.col2Tab = [];

            this.addCol1Item = function(el) {
                console.log('(col1Tab pushed)');
                $scope.col1Tab.push(el);
            };

            this.addCol2Item = function(el) {
                console.log('(col2Tab pushed)');
                $scope.col2Tab.push(el);
            };
        },
        link: {
            post: function(scope, element, attrs) {
                console.info('post-link - dropright');
                // Here, I want to move some of the elements of #col1-el
                // into #col2-el
            }
        },
        templateUrl: 'dropright-tpl.html'
    };
})

.directive('col1Item', function($interpolate) {
    return {
        require: '^dropright',
        restrict: 'E',
        transclude: true,
        controller: function() {
            console.log('-- controller - col1Item');
        },
        link: {
            post: function(scope, element, attrs, droprightCtrl) {
                console.log('-- post-link - col1Item');
                droprightCtrl.addCol1Item(element.children()[0]);
            }
        },
        templateUrl: 'col1-tpl.html'
    };      
})

.directive('col2Item', function() {
    var directiveDefinitionObject = {
        require: '^dropright',
        restrict: 'E',
        transclude: true,
        controller: function() {
            console.log('---- controller - col2Item');
        },
        link: {
            post: function(scope, element, attrs, droprightCtrl) {
                console.log('---- post-link - col2Item');
                droprightCtrl.addCol2Item(element.children()[0]);
            }
        },
        templateUrl: 'col2-tpl.html'
    };
    return directiveDefinitionObject;
});

Is there any clean way to execute the link function of a directive after all the link functions of its children while using transclusion?


回答1:


This is my theory - its not the transclude aspect that is causing the sequence issue but rather the template being a templateUrl. The template needs to be resolved before the post link function get to act on it - hence we say post link function is safe to do DOM manipulation. While we are getting 304s for all the 3 templates - we do have to read them and it ultimately resolves the template promise.

I created a plunker with template instead of templateUrl to prove the corollary. I have hot refresh/plunker Stop/Run many times but I always get link - dropright at the end.

Plunker with template instead of templateUrl

I don't pretend to understand the compile.js code fully. However it does appear that in compileTemplateUrl function $http.success() resolves the template and then on success the applyDirectivesToNode function is called passing in postLinkFn.

https://github.com/angular/angular.js/blob/master/src/ng/compile.js




回答2:


This may be just weirdness with Plunker. I tried copying the files to my local IIS, and was not able to replicate the issue.



来源:https://stackoverflow.com/questions/25241272/why-is-the-post-link-function-executed-before-the-transcluded-child-link-functio

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!