How to execute parent directive before child directive?

后端 未结 1 1712
轻奢々
轻奢々 2020-12-13 14:18

I\'m looking to write two angular directives, a parent and a child directive, to create sortable and cloneable widgets. The intended markup is:



        
相关标签:
1条回答
  • 2020-12-13 14:54

    Reasoning

    postLink() is executed in reverse order, which means the child directive's postLink() will be called before the parent's (i.e. depth first). For some reason, this is the default behavior (link() actually refers to postLink()). Luckily we also have preLink(), which works the other way around - we can utilize that to our benefit.

    To illustrate this - the following snippet of code:

    app.directive('parent', function($log) {
        return {
            restrict: 'E',
            compile: function compile(tElement, tAttrs, transclude) {
                return {
                    pre: function preLink(scope, iElement, iAttrs, controller) {
                        $log.info('parent pre');
                    },
                    post: function postLink(scope, iElement, iAttrs, controller) {
                        $log.info('parent post');
                    }
                }
            }
        };
    });
    
    app.directive('child', function($log) {
        return {
            restrict: 'E',
            compile: function compile(tElement, tAttrs, transclude) {
                return {
                    pre: function preLink(scope, iElement, iAttrs, controller) {
                        $log.info('child pre');
                    },
                    post: function postLink(scope, iElement, iAttrs, controller) {
                        $log.info('child post');
                    }
                }
            }
        };
    });
    

    … will output the following:

    > parent pre
    > child pre
    > child post
    > parent post 
    

    See it live on plunker.

    Solution

    If we want the parent directive's logic to be performed before the child's, we will explicitly use preLink():

    function SortableWidgetsDirective() {
        return {
            restrict: 'A',
            compile: function compile(tElement, tAttrs, transclude) {
                return {
                    pre: function preLink(scope, iElement, iAttrs, controller) {
                        iElement.find(".widget header").append($("<div class='widget-controls'></div>"));
                        iElement.sortable({});
                    },
                    post: angular.noop
                }
            }
        };
    }
    
    function CloneableWidgetDirective() {
        return {
            restrict: 'A',
            compile: function compile(tElement, tAttrs, transclude) {
                return {
                    pre: function preLink(scope, iElement, iAttrs, controller) {
                        iElement.find("header .widget-controls").append($("<div class='clone-handle'></div>"));
                    },
                    post: angular.noop
                }
            }
        };
    }
    

    References

    • $compile service on the AngularJS docs.

    Post Scriptum

    • You are correct, by the way - priority is meant for use with directives that share the same element.

    • angular.noop is just an empty method that returns nothing. If you still want to use the postLink() functions, just place the function declaration instead, as you would normally do, i.e.:

      post: function postLink(scope, iElement, iAttrs, controller) { ... }
      
    • Be ware of the use of templateUrl, as “ Because the template loading is asynchronous the compilation/linking is suspended until the template is loaded ” [source]. As a result, the order of execution will be disrupted. You can remedy this by including the template inlined in the template property instead.

    0 讨论(0)
提交回复
热议问题