【问题标题】:How do I use @ViewChild with an external ng-template (Angular 11)如何将 @ViewChild 与外部 ng 模板一起使用(Angular 11)
【发布时间】:2021-05-23 01:42:36
【问题描述】:

问题

所以我有两个 Angular 组件,一个父组件和一个子组件。父组件将自定义模板传递给子组件,然后子组件使用 ngTemplateOutlet 将模板与自己的数据结合起来。

这在大多数情况下都很好用。不幸的是,我在尝试从子级访问此父模板的 DOM 元素时遇到了问题。

如果我尝试使用@ViewChild('container',{static: false}) 从默认子模板访问<div #container></div>,它会毫无问题地获取元素。当我使用 app.component 传入的自定义模板执行相同操作时,我收到错误“无法读取未定义的属性 'nativeElement'”。

我还需要做什么才能访问我的模板的 DOM?

这是Stackblitz

App.Component(父)

import { Component } from "@angular/core";

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {}
<child [customTemplate]="parentTemplate"></child>

<ng-template #parentTemplate let-context="context">

    <div #container>HELLO FROM CONTAINER</div>
    <button (click)="context.toggleShow()">Toggle Display</button>
    <div *ngIf="context.canShow">Container contains the text: {{context.getContainerText()}}</div>
</ng-template>

child.component(子)

import {
  Component,
  ElementRef,
  Input,
  TemplateRef,
  ViewChild
} from "@angular/core";

@Component({
  selector: "child",
  templateUrl: "./child.component.html",
  styleUrls: ["./child.component.css"]
})
export class ChildComponent {
  @Input() public customTemplate!: TemplateRef<HTMLElement>;
  @ViewChild("container", { static: false })
  public readonly containerRef!: ElementRef;

  templateContext = { context: this };
  canShow: boolean = false;
  

  toggleShow() {
    this.canShow = !this.canShow;
  }
  getContainerText() {
    return this.containerRef.nativeElement.textContent;
  }
}
<ng-container *ngTemplateOutlet="customTemplate || defaultTemplate; context: templateContext">
</ng-container>

<ng-template #defaultTemplate>
    <div #container>GOODBYE FROM CONTAINER</div>
    <button (click)="toggleShow()">Toggle Display</button>
    <div *ngIf="canShow">Container contains the text: {{getContainerText()}}</div>
</ng-template>

我的问题

如何使用@ViewChild 从随 DOM 中的任何更改而更新的外部模板访问此 div? (注意:删除 *ngIf 不是此项目的选项)

这是什么原因造成的?我可以使用任何生命周期方法来解决此问题吗?

我的预感 我猜在 DOM 使用其新模板更新之前调用 ViewChild,我需要为 DOM 更改设置一个侦听器。我试过这个但失败了,所以我真的很感激一些关于如何最好地进行的智慧。在此先感谢:)

编辑: 无论您是传入自定义模板还是使用默认模板,此解决方案都需要正确显示&lt;div #container&gt;&lt;/div&gt;

【问题讨论】:

  • 您找到解决方案了吗?

标签: javascript html angular ng-template ngtemplateoutlet


【解决方案1】:

ViewChild 似乎没有选择渲染模板 - 可能是因为它最初不是组件模板的一部分。这不是时间或生命周期问题,只是永远无法以ViewChild 的形式使用

一种可行的方法是将模板作为内容传递给子组件,并使用ContentChildren 访问它。你订阅了ContentChildren QueryList for changes,它会在 DOM 元素被渲染时更新

然后您可以访问nativeElementdiv)。如果您愿意,可以在此处将侦听器添加到 DOM 元素,然后触发 cd.detectChanges,但这有点不寻常。处理父元素中的 DOM 更改可能会更好,并在子元素上使用常规 @Input 将所需的值传递给子元素

@Component({
  selector: "my-app",
  template: `
    <child>
      <ng-template #parentTemplate let-context="context">
        <div #container>Parent Template</div>
      </ng-template>
    </child>
  `,
  styleUrls: ["./app.component.css"]
})
export class AppComponent {}
@Component({
  selector: "child",
  template: `
    <ng-container *ngTemplateOutlet="customTemplate"> </ng-container>
  `,
  styleUrls: ["./child.component.css"]
})
export class ChildComponent implements AfterContentInit {
  @ContentChild("parentTemplate")
  customTemplate: TemplateRef<any>;

  @ContentChildren("container")
  containerList: QueryList<HTMLElement>;

  ngAfterContentInit() {
    this.containerList.changes.subscribe(list => {
      console.log(list.first.nativeElement.innerText);
      // prints 'Parent Template'
    });
  }
}

【讨论】:

  • 试过这个。它适用于自定义模板,但现在破坏了默认子模板。无论是否通过自定义模板,该解决方案都需要工作。这是我对您的解决方案的实施 (stackblitz.com/edit/angular-ivy-fdakal)
猜你喜欢
  • 2016-05-17
  • 2018-07-03
  • 2017-07-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多