Angular 2 transclusion:我可以将插槽内容向上传递给父组件吗

Fra*_*cke 6 transclusion angular

我有一个外部组件(蓝绿色),其中包含一些带有我自己的ui-button按钮的flexbox 工具栏。还有一个内部组件,主要在棕色区域做它的事情(正如你所期望的)。

但是,根据内部组件(有几个来回切换的组件),必须在顶部/底部栏中插入更多上下文按钮。

(在外部使用绝对定位内容的 CSS 技巧不是一种选择,根据大小和便利性,外部工具栏的位置、大小等可能会有很大差异......)


现在我的问题是:

我可以以某种方式传递对某些占位符(黑色方括号)的引用(就像常规内容投影/嵌入一样),并让它们由来自子组件的内容填充?

也许像ngTemplate[Outlet]这样的东西?和/或使用 @Output

我想“向上传递”多于plain textsimple <b>rich</b> <i>html</i>理想情况下真正的角度模板代码,包括自定义组件,如

    <ui-button icon="up.svg" (click)='...'></ui-button>
    <ui-button icon="down.svg" (click)='...'></ui-button>
Run Code Online (Sandbox Code Playgroud)

...在外部组件的顶部栏中引导到:

<section class='some-flexbox'>
    <ui-button icon="home.svg" (click)='...'></ui-button>

    <ui-button icon="up.svg" (click)='...'></ui-button>
    <ui-button icon="down.svg" (click)='...'></ui-button>

    <ui-button icon="help.svg" (click)='...'></ui-button>
    <ui-button icon="account.svg" (click)='...'></ui-button>
</section>
Run Code Online (Sandbox Code Playgroud)

想一想,添加的那些按钮的点击事件应该是找到了回家的路子,叹息……

更新

ngTemplateOutlet 听起来很有趣

我们还可以使用 ngTemplateOutlet 指令获取模板本身并在页面上的任何位置实例化它:

检查中,仍然不知道如何...

Hir*_*ekh 6

有两种可能的方法可以实现所需的行为。

  1. 使用 Angular CDK 的PortalModule
  2. 通过创建一个使用 javascript dom api 将元素从子组件移动到父组件的自定义指令。

这是这两种解决方案的快速解释。

1.使用PortalModule 演示链接

您可以在子组件的模板文件中定义模板。

<ng-template #templateForParent>
    <button (click)="onTemplateBtnClicked('btn from child')">
    Button from Child
  </button>
</ng-template>
Run Code Online (Sandbox Code Playgroud)

然后使用装饰器在子组件文件中获取该模板的引用@viewChid

@ViewChild("templateForParent", { static: true }) templateForParent: TemplateRef<any>;
Run Code Online (Sandbox Code Playgroud)

然后您可以TemplatePortal使用该模板引用创建一个门户,并在需要时将该门户传递给父级。

ngAfterViewInit(): void {
    const templatePortal = new TemplatePortal(this.templateForParent, this.viewContainerRef);
    setTimeout(() => {
      this.renderToParent.emit(templatePortal);
    });
  }
Run Code Online (Sandbox Code Playgroud)

父组件的模板文件可能如下所示。

<div fxLayout="row" fxLayoutAlign="space-between center">
  <button (click)="onBtnClick('one')">one</button>
  <button (click)="onBtnClick('two')">two</button>
  <ng-container [cdkPortalOutlet]="selectedPortal"></ng-container> <!-- pass the portal to the portalOutlet -->
  <app-button-group (renderToParent)="selectedPortal = $event"></app-button-group>
  <button (click)="onBtnClick('five')">five</button>
</div>
Run Code Online (Sandbox Code Playgroud)

就是这样。现在,您已将模板呈现给父组件,并且该组件仍然具有click事件绑定。单击事件处理程序是在子组件内部定义的。

使用ComponentPortalDomPortal 的方式相同

2. 使用自定义指令 演示链接

您可以按如下方式创建一个角度指令,该指令会将元素从其宿主组件移动到宿主组件的父级。

import {Directive,TemplateRef,ElementRef,OnChanges,SimpleChanges,OnInit,Renderer2,DoCheck} from "@angular/core";

@Directive({
  selector: "[appRenderToParent]"
})
export class RenderToParentDirective implements OnInit {
  constructor(private elementRef: ElementRef<HTMLElement>) {}

  ngOnInit(): void {
    const childNodes = [];
    this.elementRef.nativeElement.childNodes.forEach(node =>
      childNodes.push(node)
    );
    childNodes.forEach(node => {
      this.elementRef.nativeElement.parentElement.insertBefore(
        node,
        this.elementRef.nativeElement
      );
    });

    this.elementRef.nativeElement.style.display = "none";
  }
}
Run Code Online (Sandbox Code Playgroud)

然后您可以在任何组件上使用该指令。

<div fxLayout="row" fxLayoutAlign="space-between center">
  <button (click)="onBtnClick('one')">one</button>
  <button (click)="onBtnClick('two')">two</button>
  <app-button-group appRenderToParent></app-button-group>
  <button (click)="onBtnClick('five')">five</button>
</div>
Run Code Online (Sandbox Code Playgroud)

这里<app-button-group>有以下模板文件。

<button (click)="onBtnClick('three')">three</button>
<button (click)="onBtnClick('four')">four</button>
Run Code Online (Sandbox Code Playgroud)

因此,我们的指令会将按钮元素移动到其宿主的父组件。我们只是在 DOM 树中移动 DOM 节点,因此与这些元素绑定的所有事件仍然有效。

我们可以修改指令以接受类名或 id,并仅移动那些具有该类名或 id 的元素。

我希望这个能帮上忙。我建议阅读文档以获取有关 PortalModule 的更多信息。