角度:在进行内容投影时获取对父组件的引用

时间:2018-07-12 06:20:54

标签: angular

在我的情况下,如果某个组件位于特定组件内部,则其行为应有所不同。因此,我想浏览一下父母,以找到正确类型的组件,该组件通过简单情况下的依赖注入可以很好地工作:

子组件

@Component({
    selector: 'spike-child',
    template: `<div>I am the child, trying to find my parent. 
        <button (click)="log()">Test</button></div>`
})
export class ChildComponent {
    // Get Parent through dependency injection; or null if not present.
    public constructor(@Optional() private parent: ParentComponent) { }

    public log(): void {
        console.log('Parent', this.parent);
        console.log('Parent.Id', this.parent && this.parent.id);
    }
}

父组件

@Component({
    selector: 'spike-parent',
    template: `
    <div>
        <div>I am the parent of the content below, ID = {{ id }}:</div>
        <ng-content></ng-content>
    </div>`
})
export class ParentComponent {
  @Input()
  public id: number;
}

用法,有效

<spike-parent [id]="1">
  <spike-child></spike-child>
</spike-parent>

不幸的是,如果像这样通过内容投影添加一个间接访问,则此操作将不再起作用:

ProjectedContent组件

@Component({
    selector: 'spike-projected-content',
    template: '<spike-parent [id]="2"><ng-content></ng-content></spike-parent>'
})
export class ProjectedContentComponent { }

用法,不起作用

  <spike-projected-content>
    <spike-child></spike-child>
  </spike-projected-content>

很明显,在运行时,子级将再次位于父级中,但是现在它总是作为注入的参数为null。我了解所计划的内容将保留原始上下文(包括注入器链),这几乎总是有用的,但是有什么方法可以解决这种情况?我阅读了有关ngComponentOutlet的信息,它看起来可能会有所帮助,但我没有完全看到它的适用范围。我也没有发现任何问题可以解决这种情况。我想我可以查询DOM以获得结果,但是我显然想避免这种情况,并为此使用Angular机制。

非常感谢您!

3 个答案:

答案 0 :(得分:2)

我只能看到两种方式(DOM黑客除外):

  1. 切换到使用模板。工单仍处于打开状态:14935。当前无法在viewContainerRef.createEmbeddedView中覆盖注入器,但是可以传递上下文信息(例如context-parameter-in-angular或使用NgTemplateOutlet)。
  2. 使用DI可以找到计划的子组件并调用其方法。每个子组件都使用通用令牌{provide: Token, useExisting: forwardRef(() => Child)}提供自身,它允许使用@ContentChildren(Token)并获取所有投影子组件的列表,并调用任何方法/设置器。

答案 1 :(得分:1)

从组件的角度来看,ChildComponentParentComponent的兄弟姐妹,他们没有父子关系。都是具有共享父级ProjectedContentComponent

的组件

在DOM中,它们的HTMLElement表示形式是父/子顺序,但这与组件结构是分开的。我们可以detatch()任何组件的HTML并将其放置在DOM中的任何位置-这就是ng-content所做的

  1. 使用共享的父级(ProjectedContentComponent)在同级之间传递引用: StackBlitz Demo

  2. 组件结构展示了共享的父级而不是父子关系 StackBlitz Demo

答案 2 :(得分:0)

为了解决眼前的难题,我现在开始查询DOM。这感觉确实很棘手,我将不胜感激其他建议,但我也想与任何有类似案件的人分享我的解决方案。

注意::如果我的组件位于其他组件内部,则我的解决方案只会发现 。它没有引用该组件的实例,因为在我的情况下我不需要它。

提供以下功能:

private hasParent(element: ElementRef, selector: string): boolean {
    let parent = element.nativeElement;
    while(parent = parent.parentElement) {
        if (parent.matches(selector)) { return true; }
    }

    return false;
}

我们可以找出我们的组件是否在这样的特定其他组件内:

public constructor(element: ElementRef) {
    const isChildOfSpikeParent = this.hasParent(element, 'spike-parent');

我还尝试了以下方法来消除硬编码的选择器,但是它不适用于AOT,因此我放弃了该方法:

function getSelectorOfComponent(componentType: Function): string {
    // Reading annotations, see: https://stackoverflow.com/questions/46937746/how-to-retrieve-a-components-metadata-in-angular
    const decorators: any[] = (<any>componentType).__annotations__;
    const componentDecorator = decorators && decorators.find(d => d.ngMetadataName === 'Component');

    return componentDecorator && componentDecorator.selector;
}