【问题标题】:How to execute parent directive before child directive?如何在子指令之前执行父指令?
【发布时间】:2014-02-27 21:43:48
【问题描述】:

我正在寻找编写两个角度指令,一个父指令和一个子指令,以创建可排序和可克隆的小部件。预期的标记是:

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

但是,子指令似乎在父元素之前执行,在某个元素可用之前(由父元素添加):

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>"));
        }
    };
}

如您所见,我尝试设置优先级,但我认为因为它们位于不同的元素上,所以它不起作用。

如何让父级先执行?

【问题讨论】:

    标签: javascript angularjs angularjs-directive


    【解决方案1】:

    推理

    postLink() 以相反的顺序执行,这意味着子指令的 postLink() 将在父指令之前被调用(即深度优先)。出于某种原因,这是默认行为(link() 实际上指的是postLink())。幸运的是,我们也有 preLink(),它的工作方式正好相反——我们可以利用它为我们带来好处。

    为了说明这一点——下面的sn-p代码:

    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');
                    }
                }
            }
        };
    });
    

    ... 将输出以下内容:

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

    live on plunker

    解决方案

    如果我们希望父指令的逻辑在子指令之前执行,我们将显式使用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
                }
            }
        };
    }
    

    参考文献

    • $compile AngularJS 文档中的服务。

    后记

    • 顺便说一句,你是对的 - priority 用于与共享相同元素的指令一起使用。

    • angular.noop 只是一个不返回任何内容的空方法。如果您仍想使用 postLink() 函数,只需像往常一样放置函数声明,即:

      post: function postLink(scope, iElement, iAttrs, controller) { ... }
      
    • 注意templateUrl的使用,因为“因为模板加载是异步的,所以编译/链接会暂停,直到加载模板” [source]。结果,执行顺序将被打乱。您可以通过在 template 属性中包含内联模板来解决此问题。

    【讨论】:

    • 你知道这在提供 templateUrl 时会完全崩溃吗? plnkr.co/edit/1OlW6UuhvkfzD27yz2Vc?p=preview 这对我来说是个问题,因为我需要使用一个。你有什么建议?
    • 是的,请参阅另一个更新 - 值得注意的是,在答案正文中。
    • 实际上我的模板已经在a&lt;script type="text/ng-template" id="my-template&gt;&lt;/script&gt;的页面上。我可以强制 Angular 不异步加载它来解决这个问题吗?
    • 我解决这个问题的方法是使用模板,就像提到的那样。但是要注入 $templateCache 并做模板:$templateCache.get('url.tpl.html')。保持前/后链接顺序的巧妙小技巧。
    • 这个答案对我仍然有用。我想为这些指令添加控制器初始化的顺序。顺序为:父控制器,父pre,子控制器,子pre,子post,父post,
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多