javascript - 如何将 @ViewChild 与外部 ng 模板一起使用(Angular 11)

标签 javascript html angular ng-template ngtemplateoutlet

问题
所以我有两个 Angular 组件,一个父组件和一个子组件。父组件将自定义模板传递给子组件,然后子组件使用 ngTemplateOutlet 将模板与自己的数据结合起来。
这在大多数情况下效果很好。不幸的是,我在尝试从子级访问此父模板的 DOM 元素时遇到了问题。
如果我尝试访问 <div #container></div>从默认子模板使用 @ViewChild('container',{static: false}) ,它可以毫无问题地获得元素。当我使用 app.component 传入的自定义模板执行相同操作时,我收到错误“无法读取未定义的属性‘nativeElement’”。
我还需要做什么才能访问我的模板的 DOM?
这是 Stackblitz
应用程序组件(父)

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 不是此项目的选项)
这是什么原因造成的?我可以使用任何生命周期方法来解决此问题吗?
我的预感
我猜测 ViewChild 在 DOM 使用其新模板更新之前被调用,我需要为 DOM 更改设置一个监听器。我试过这个但失败了,所以我真的很感激一些关于如何最好地进行的智慧。提前致谢 :)
编辑:
此方案需要正确显示<div #container></div>无论您是传入自定义模板还是使用默认模板。

最佳答案

ViewChild似乎没有选择渲染模板 - 可能是因为它最初不是组件模板的一部分。这不是时间或生命周期问题,它只是永远无法作为 ViewChild 使用。
一种可行的方法是将模板作为内容传递给子组件,并使用 ContentChildren 访问它。 .您订阅ContentChildren QueryList对于 changes ,它将在 DOM 元素被渲染时更新
然后您可以访问 nativeElement (div)。如果您愿意,可以在此处将监听器添加到 DOM 元素,并触发 cd.detectChanges之后,但这有点不寻常。处理父元素中的 DOM 更改可能会更好,并使用常规 @Input 将所需的值传递给子元素。在 child 身上

@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'
    });
  }
}

关于javascript - 如何将 @ViewChild 与外部 ng 模板一起使用(Angular 11),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66288186/

相关文章:

javascript - 我想更改 : #shadow root (user-agent) 显示的 HTML 文本

angular - RxJS 根据先前的数据响应执行请求(涉及对象数组的循环)

angular - 在 Angular 中安装 Tesseract.js 后找不到名称 'Buffer' 错误

javascript - 加载内联 jQuery 脚本以及延迟和异步脚本

javascript - 始终在后台运行的桌面应用程序

php - Javascript:发布到新窗口

javascript - 重绘事件网格

javascript - 从本地文件系统加载 JSON 文件时出现 "Unexpected end of JSON input"错误

html - Materialize 下拉列表落后于其他元素溢出且 z-index 不适用

html - CSS 在打印中创建整页无边距纯色背景