首页 > 解决方案 > 如何在 Angular 6 中实现延迟组件投影?

问题描述

这是我将引用的代码示例。这是一个 stackblitz 项目,被剥离为解释问题所需的裸露组件。

所以我想做的是能够将一个组件作为另一个组件的子组件传递,但是这个父组件应该能够阻止它的创建,直到它决定要创建它。在示例中,app.component.html 文件是我<app-action>作为孩子传递给的,<app-projector>并且我希望<app-projector>能够推迟<app-action>一段时间的实际创建。

<app-projector>组件实际上ActionComponent不能通过 ContentChildren 引用 ts 类,因为它应该可以是任何组件。

我当前的 stackblitz 示例尝试<ng-template>在投影仪组件中使用,但正如您所见,它确实在投影仪组件决定实际渲染模板中的内容之前创建组件并运行组件生命周期函数。我想让它在我们准备好渲染作为内容传递的组件之前,我们不会启动生命周期函数或创建组件。

有任何想法吗?

标签: angular

解决方案


您的问题的根源是<ng-content>在构建时发生 - 您投射到其中的任何组件都将在构建时而不是在运行时创建!(来源) 这就是为什么在你的 StackBlitz 中,<app-action>组件甚至在它显示在屏幕上之前就已经开始计数了。而不是<ng-content>用于延迟内容投影,您应该使用以下方法。

(查看StackBlitz 演示 ,看看它是否按预期工作)


创建结构指令

结构指令是改变 DOM结构的指令,例如通过添加、删除或操作元素。例如,ngIfngFor是结构指令。关于结构指令的事情是,如果你在指令前面放一个星号前缀,Angular 会自动将指令的宿主元素“转换”为一个。例如,这个:<ng-template>*

<div *ngIf="myCondition">Lorem ipsum</div>

...由 Angular 自动转换为:

<ng-template [ngIf]="myCondition">
  <div>Lorem ipsum</div>
</ng-template>

因此,如果我们创建了自己的结构指令delayedContent并将其应用于组件/元素,例如<app-action>

<app-action *delayedContent></app-action>

...然后它将转换为:

<ng-template delayedContent>
   <app-action></app-action>
</ng-template>

这是 TS 文件delayedContent

@Directive({
  selector: '[delayedContent]'
})
export class DelayedContentDirective {
  constructor(templateRef: TemplateRef<void>, projector: ProjectorComponent) {
    projector.allDelayedContent.push(templateRef);
  }
}

在这里,我可以使用Angular 生成TemplateRef的元素来获取引用<ng-template>,然后将其推送到父组件TemplateRef中的数组。<app-projector>

<app-projector>组件中,我们现在可以将TemplateRefs它们显示在您的<ng-container>. 现在您的<app-action>组件只会在createEmbeddedView()被调用时创建,因此它会在显示时从 0 开始计数(而不是从 3 开始)。

@Component({
  selector: 'app-projector',
  templateUrl: './projector.component.html'
})
export class ProjectorComponent implements AfterViewInit {
  // All the TemplateRefs to the delayed content will be stored in this array
  allDelayedContent: TemplateRef<void>[] = [];

  @ViewChild('container', { read: ViewContainerRef }) _container: ViewContainerRef;

  constructor() { }

  ngAfterViewInit() {
    // Show this after 3 seconds
    setTimeout(() => {
      this._container.createEmbeddedView(this.allDelayedContent[0]);
    }, 3000)

    // You can add other elements to the `allDelayedContent` array and show them here
    // Show this after 5 seconds
    setTimeout(() => {
      this._container.createEmbeddedView(this.allDelayedContent[1]);
    }, 5000)

    // Show this after 7 seconds
    setTimeout(() => {
      this._container.createEmbeddedView(this.allDelayedContent[2]);
    }, 7000)
  }
}

查看第二个 StackBlitz 演示。它具有delayedContent结构指令的修改版本,可让您直接在 HTML 中指定延迟时间,如下所示:

<app-projector>
  <app-action *delayedContent="3000"></app-action>
  <div *delayedContent="2000">Some delayed content</div>
  <div *delayedContent="6000">More delayed content</div>
<app-projector>

推荐阅读