【问题标题】:Inject parent component of the same type as child component注入与子组件同类型的父组件
【发布时间】:2016-06-12 23:01:14
【问题描述】:

在 Angular 2 中,子组件可以通过构造函数参数注入其父组件。示例:

@Component({...})
export class ParentComponent {
  ...
}

@Component({...})
export class ChildComponent {
  constructor(private parent: ParentComponent) { }
  ...
}

只要父母和孩子是不同的类型,这很好用。

但是,另一个典型用例是树结构,其中每个树节点都显示为一个单独的组件。如果每个树节点组件都应该可以访问其父节点,我们应该怎么做?我试过这个:

@Component({...})
export class TreeNodeComponent {
  constructor(private parent: TreeNodeComponent) { }
...
}

但这会失败并出现以下运行时异常:

EXCEPTION: Cannot instantiate cyclic dependency!

我猜原因是 Angular 2 注入了组件本身而不是其父组件。

如何告诉 Angular 注入组件的父组件,即使它们是相同类型的?

Plunker https://plnkr.co/edit/ddvupV?p=preview

【问题讨论】:

标签: dependency-injection angular


【解决方案1】:

这样就可以了

constructor(@SkipSelf() @Host() @Optional() parent: TreeNodeComponent) {}

Plunker

  • @SkipSelf() 是不让自己注射,如果请求 TreeNodeComponent 则适用
  • @Host() 不要看比宿主元素更远的地方
  • @Optional() ??根节点没有父节点TreeNodeComponent

另见http://blog.thoughtram.io/angular/2015/08/20/host-and-visibility-in-angular-2-dependency-injection.html

【讨论】:

  • 她是个笨蛋:link。阅读 treenode.component 中的注释。
  • 我理解您对策略的评论,我完全同意。在像这样的树结构的情况下,它会使用绑定。我的实际用例是不同的。我只是以树节点为例来说明核心挑战,因为树结构是一个众所周知的概念。
  • 好的,找到了。我错过了@SkipSelf()(以及根节点所必需的@Optional()
  • 如果这解决了您的问题,您是否介意接受表明您的问题已得到解答的答案(向上/向下投票按钮下方的白色复选标记)。谢谢。
  • 从来不知道@SkipSelf()!谢谢@Gunter!我有 @Optional() 和 @Host() 但错过了这个重要的部分。
【解决方案2】:

Angular2 在当前注入器中查找提供者。在您的情况下, TreeNodeComponent 对应于组件本身。

父组件实例位于父注入器中。

我认为你可以尝试注入一个 Injector 类来访问父注入器,然后获取父组件的实例。类似的东西:

@Component({
  (...)
})
export class TreeNodeComponent {
  constructor(injector:Injector) {
    let parentInjector = injector.parent;
    let parent = patentInjector.get(TreeNodeComponent);
  }
}

有关 Injector 类的文档,请参阅此链接:

也就是说,我认为 Gunter 关于绑定和共享服务的评论特别相关......

【讨论】:

    【解决方案3】:

    感谢 Günther,此帖子已得到解决。不过,我想根据收到的架构反馈进行跟进。

    首先,我完全同意 TreeNodeComponent 用例示例是一种反模式。像树这样的数据驱动组件应该由数据绑定控制。对于这种反模式,我深表歉意。

    但是,我的实际用例(解释起来更复杂)是我想开发一个高级下拉菜单。要求:

    • 下拉菜单应具有任意数量的级别(菜单、子菜单、子子菜单等)
    • 下拉菜单是在设计时定义的 - 它的内容不是基于动态数据。
    • 应该可以将事件处理程序 (_)= 附加到每个下拉项。
    • 应该可以将自定义控件组件插入到下拉列表中。

    使用示例:

    <dropdown label="Actions">
      <dropdown-item label="Action 1" (click)="action1()"></dropdown-item>
      <dropdown-item label="Action 2" (click)="action2()"></dropdown-item>
      <dropdown-item label="Submenu">
        <dropdown-item label="Action 3.1" (click)="action31()"></dropdown-item>
        <dropdown-item label="Action 3.2">
          <dropdown-slider (change)="changeHandler()"></dropdown-slider>
        </dropdown-item>
        <dropdown-item label="Submenu">
          <dropdown-item label="Action 3.3.1" (click)="action331()"></dropdown-item>
          <dropdown-item label="Action 3.3.2" (click)="action332()"></dropdown-item>
        </dropdown-item>  
      </dropdown-item>  
    </dropdown>    
    

    我的考虑是使用数据绑定很难实现。能够以这种方式绑定事件处理程序并包含自定义组件非常实用。

    但我可能错了!非常欢迎对此提出任何意见!

    【讨论】:

    • 这看起来不像是一个答案。我认为编辑您的问题并在那里添加此内容会更好。
    • 你为什么要注入父母?您想从父级访问哪些成员?
    • 我需要注入父项,因为打开一个菜单项应该关闭所有同级项,以便同时打开一个“菜单路径”。为了实现这一点,我必须能够调用父组件。
    • 您可以在&lt;child&gt; element for that and bind a method to it. 上使用@Output()` 为了将值从父级传递给子级,您添加一个@将 987654324@ 绑定到孩子并将其绑定为 &lt;child [someInputOnChild]="someValueFromParent"&gt; ,其中值可以是一切,甚至是函数。
    猜你喜欢
    • 2016-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-01
    • 1970-01-01
    • 2017-05-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多