Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
137 views
in Technique[技术] by (71.8m points)

javascript - How to execute parent directive before child directive?

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

<div class="widget-container" data-sortable-widgets>
      <section class="widget" data-cloneable-widget></section>
<div>

However, the child directive seems to execute before the parent, before a certain element is available (its added by the parent):

function SortableWidgetsDirective() {
    return {
        priority: 200,
        restrict: 'A',
        link: function ($scope, element, attrs) {
            element.find(".widget header").append($("<div class='widget-controls'></div>"));
            element.sortable({  });
        }
    };
}

function CloneableWidgetDirective() {
    return {
        priority: 100,
        restrict: 'A',
        link: function ($scope, element, attrs) {
            // This directive wrongfully executes first so widget-controls is no available
            element.find("header .widget-controls").append($("<div class='clone-handle'></div>"));
        }
    };
}

As you can see i tried setting priority but I think because they're on different elements, it does not work.

How can I make the parent execute first?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...