为什么ng-transclude指令的作用域不是它所在指令作用域的子作用域——如果该指令有一个隔离作用域?

31

给定一个具有 transclude 和隔离作用域的指令(container1),当该指令被链接时,我有以下这些作用域:

Scope 004           <-- scope of the body
    Scope 005       <-- scope of directive container1
    Scope 006       <-- scope of the ng-transclude
我期望:
Scope 004            <-- scope of the body
    Scope 005        <-- scope of the directive
         Scope 006   <-- scope of the ng-transclude

如果相同的指令具有共享作用域而不是隔离作用域,我会得到预期的结果。

这给我带来了问题,因为如果被转移的内容包含另一个具有隔离作用域的指令(component1),我将得到:

Scope 004             <-- scope of the body
    Scope 005         <-- scope of the directive
    Scope 006         <-- scope of the ng-transclude
          Scope 007   <-- scope of directive component1

我希望能够像这样使用指令:

<container1>
   <component1 data="objectExposedInContainer1"/>
</container1>

但是在component1内部,$scope.dataundefined,因为objectExposedInContainer1不在正确的作用域内。

我有两个问题:

  • 如果指令使用了隔离作用域,为什么ng-transclude的作用域不是其指令作用域的子级?这是一个错误吗?
  • 如果这不是错误,那么容器指令如何将数据传递给其内容,而不是像我尝试的设置属性。

这里有一个示例,它无法正常工作:http://plnkr.co/edit/NDmJiRzTF9e5gw8Buht2?p=preview。由于Plunker是使用Angular构建的,所以很难使用Batarang进行调试。建议下载代码并注释掉app.js第10行,以使用共享作用域使其正常工作。


  1. 不,你得到了你所要求的 - 指令的作用域是隔离的。
  2. 使用共享控制器。
- JoeG
@Joe Gauterin,看看这个新的示例:http://plnkr.co/edit/Bv7B4OokkLi8bIctCIl3。在这里,`container1`包含`component1`,但没有使用`ng-transclude`。这一次,即使两者都有隔离作用域,它们的作用域也具有正确的父/子关系。`ng-transclude`的存在改变了结果。 - Sylvain
3个回答

29
为什么ng-transclude的作用域不是指令作用域的子级,如果该指令具有隔离的作用域?
ng-transclude旨在允许指令与任意内容一起使用,隔离范围旨在允许指令封装其数据。
如果ng-transclude不保留这样的作用域,那么您传递的任意内容将需要知道指令的实现细节(即它需要知道在您创建的隔离作用域上可用的内容)。
如果容器指令和包含的指令是耦合的-即您编写了两个指令并需要它们一起运作-则它们应通过共享控制器进行通信。
如果容器指令应将内容注入到子级的作用域中(例如,ng-repeat),则不应使用隔离作用域。
Angular文档非常清楚行为应该是什么:
“在典型的设置中,小部件创建了一个隔离作用域,但传输不是隔离作用域的子级,而是同级。这使得小部件可以拥有私有状态,并且传输可以绑定到父级(预隔离)作用域。”

1
感谢您提供这个全面的答案。 - Sylvain
我创建了一个解决方法,但不确定是否是最佳实践,希望您们能抽出一些时间来检查一下?这是一个自定义的 transclude hack,在链接阶段保证创建一个新的子隔离 scope。 这将创建父->子层次结构,以便我们可以在控制器中使用 $emit 和 $on。 https://gist.github.com/meanJim/1c3339bde5cbeac6417d - Jim
1
我也采用了类似@Jim的方法,但我不确定它是否是最佳实践。您可以在此处找到一个plunk:http://plnkr.co/edit/Ph5lMl0ol8ayXOFr7eal - mithun_daa

12

您可以手动转换子元素

link: function(scope, element, attrs, ctrl, transclude) {
    transclude(scope, function(clone, scope) {
        element.find('.transclude-placeholder').append(clone);
    });
}

能否提供一个完整的示例呢?.transclude-placeholder是什么? - Peter

5

嗨,我刚刚将示例更新到1.3.15,但它仍然有同样的问题。 http://plnkr.co/edit/PGsJRngCTCzstz85V80C?p=preview。我也尝试了1.4版本,结果也一样。你有什么评论吗? - Sylvain
事实上,今天我提出了一个问题,基于这个答案,我自己提供了一个答案!http://stackoverflow.com/questions/38267288/force-scope-to-named-slot-with-ng-transclude/38273990#38273990 - Matías Fidemraizer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接