【问题标题】:AngularJS: Why can't I add an attribute to grandparent element when parent has ngIf directiveAngularJS:为什么当父级有 ngIf 指令时我不能向祖父级元素添加属性
【发布时间】:2025-12-29 21:40:11
【问题描述】:

我正在使用 vanilla AngularJS v1.4.5(无 jQuery),并希望我的自定义指令在编译时为其祖父元素添加一个属性。

在编译函数中,我可以使用elementparent()方法两次获取祖父元素,并使用attr()方法添加我的属性。但是,如果 parent 元素具有 ngIf 指令,则祖父元素不会获得该属性。

angular.module('myApp', [])
    .directive('foo', fooDirective)
;

function fooDirective() {
    return {
        compile : compile,
        priority: 601 // ngIf is 600
    };

    function compile(element, attrs) {
        var parent, grandparent;

        parent = element.parent();            
        grandparent = parent.parent();

        parent.attr('foo', 'bar');
        grandparent.attr('foo', 'bar');
    }
}

JSFiddle

这是我所知道的:

  • 如果父元素上未使用ngIf,则该属性将添加到祖父元素。
  • 该问题不应与 scope 相关,因为这是在编译阶段发生的,在作用域与任何元素相关联之前。
  • 我的编译函数应该在ngIf之前运行,它的优先级为600(和doesn't have a compile function)。
  • ngIf 完全删除并重新创建 DOM 中的元素(连同其子元素),但这不应影响祖父元素或更改其属性。

如果父元素具有ngIf 指令,谁能向我解释为什么我不能将属性添加到指令的祖父元素?

【问题讨论】:

  • 只是为了成为一个坚持不懈的 Angular 默认使用 jqLit​​e 所以无论你喜欢与否你都在使用 jQuery ;)
  • 指令通常只处理它们的 DOM 子树。尝试改变祖先是很奇怪的。你也不应该这样做DOM manipilation in compile other than to the template。你的用例是什么?至于ngIf - 在编译阶段,ngIf 包含了你的指令,该指令在那时编译它,但元素在链接阶段之前还没有在 DOM 中
  • @NewDev 属性位是其中的一部分,但我的实际目标是通过移动自定义指令元素来修改模板,使其成为父元素的兄弟元素,与子元素相对。关于您对 transclude 的评论:我的印象是 transclude 函数在 compile 函数之后运行。你确定它之前运行过?
  • @ductiletoaster 是一个更大的坚持者,jqLit​​e 是 jQuery 的一个分支版本,具有自己的特性......所以除非你包含它,否则你没有使用 jQuery。 :-)
  • @ShaunScovil,您能否在问题中附加一个用例 - 这可能会有所帮助,因为您尝试做的事情似乎有些不自然。为指令提供被嵌入内容的克隆的 transclude 函数在链接时运行(因此,在编译之后),但内容的嵌入本身(即内容的提取和编译)发生在 compile-时间。

标签: javascript angularjs angularjs-directive angularjs-compile jqlite


【解决方案1】:

我还不完全确定为什么会发生这种情况,但是你有什么特别的原因为什么你用编译来做这个?我调整了您的指令以使用链接,它似乎工作得很好。

(function () {
    'use strict';

    angular.module('myApp', [])
        .directive('foo', fooDirective)
    ;

    function fooDirective() {
        return {
            link : link,
            priority: 601 // ngIf is 600
        };

        function link($scope, element, attrs) {
            var parent, grandparent;

            parent = element.parent();            
            grandparent = parent.parent();           
            parent.attr('foo', 'bar');
            grandparent.attr('foo', 'bar');
        }
    }

})();

编辑: 就像@NewDev 所说,您通常应该在链接阶段而不是在编译期间进行 DOM 操作。

【讨论】:

  • 是的,除了添加一个属性之外,我还尝试修改模板,以便带有我的自定义指令的元素成为父级的兄弟。我认为我不能在链接函数中做到这一点,因为如果 ngIf 的值评估为 false,我的子元素将从 DOM 中删除。
  • 是否每次都不想移动“子”元素?在我看来,“孩子”可能应该是指令的模板,然后在链接时动态添加到 DOM 中。最后,将您的指令设置在此父级而不是此子级上,以避免整个混乱。我可能过于简化了,但也许你可以解释你试图完成的整个过程
  • @ShaunScovil,但您的问题只是关于添加属性。这个答案提供了解决方案。如果 ng-if 将您的孩子从 DOM 中移除,那实际上是“设计使然”——也就是说,这将是使用 ng-if 的人的期望。所以,我认为这解决了你所说的问题,但是为了解决更大的问题(使元素成为祖父母的兄弟姐妹),我建议你提出一个新问题并解释你想要做什么。否则,我们可能会解决XY question
  • @NewDev 要清楚,我的问题是“为什么我不能添加属性......”而不是“我如何......”,所以你对这个问题的评论或多或少我正在寻找的答案——尽管我现在遇到了互联网连接问题并且只有我的手机可以使用。如果您可以解释或参考一个文档,该文档解释了有关 transclude 生命周期的更多信息,我会接受。
【解决方案2】:

所以,重申一下,问题是,为什么给出以下内容:

<grand-parent>
  <parent ng-if="condition">
    <foo></foo>
  </parent>
</grand-parent>

当尝试从foo 的编译中检索var grandparent = tElement.parent().parent() 时,grandparent 不引用&lt;grand-parent&gt; 元素。

答案是因为ngIf-引起的transclusion,即使condition === true

嵌入是将内容(或元素 + 内容,取决于嵌入的类型)从 DOM 中拉出、编译,然后作为克隆提供给嵌入函数的过程,该函数本身可作为link函数的第5个参数使用:

link: function(scope, element, attrs, ctrls, transcludeFn){
  transcludeFn(scope, function cloneAttachFn(clonedContent){

    // clonedContent is the subtree that was transcluded, compiled and cloned
    element.append(clonedContent);
  });
}

因此,编译过程从&lt;grand-parent&gt; 开始,然后转到&lt;parent&gt;,在那里它看到一个指令-ngIf。因为ngIftransclude: "element",所以它将&lt;parent&gt;&lt;foo&gt;&lt;/foo&gt;&lt;/parent&gt; 从DOM 中拉出,并对其进行编译。因此,编译继续编译&lt;parent&gt; 上的其他低优先级指令(如果可用),然后编译foo 指令。

此时,&lt;foo&gt; 不在&lt;grand-parent&gt; 之下,tElement.parent().parent() 产生[]

【讨论】:

最近更新 更多