5

问题

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

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

如果我尝试使用<div #container></div>从默认子模板访问@ViewChild('container',{static: false}),它会毫无问题地获取元素。当我使用 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 更改设置一个侦听器。我试过这个但失败了,所以我真的很感激一些关于如何最好地进行的智慧。提前致谢 :)

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

4

1 回答 1

2

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

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

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

@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'
    });
  }
}
于 2021-02-20T12:01:32.270 回答