写点什么

关于 Angular view Query 的 id 选择器问题的单步调试

作者:Jerry Wang
  • 2022 年 10 月 08 日
    四川
  • 本文字数:1436 字

    阅读完需:约 5 分钟

关于 Angular view Query 的 id 选择器问题的单步调试

问题描述

我有这样一个 Angular Component,模板文件如下:@Component({selector: 'example-app',template: `<pane id="1" *ngIf="shouldShow"></pane><pane id="2" *ngIf="!shouldShow"></pane>


<button (click)="toggle()">Toggle</button><div id="panel 1?" #pane999>1</div><div id="panel 2?" pane>2</div><div>Selected: {{selectedPane}}</div>
复制代码


`,})



在其 Component 实现里,我期望通过 `@ViewChild`,在运行时拿到 id 为 `panel 1?` 的 div 元素的实例。```typescriptexport class ViewChildComp {
constructor(public changeDetectorRef: ChangeDetectorRef ){} @ViewChild("panel 1?") set panethis(v) { //setTimeout(()=> this.selectedPane = v.id, 0); this.selectedPane = v.id; this.changeDetectorRef.detectChanges();
//Promise.resolve().then(() => this.selectedPane = v.id); }
复制代码


然而运行时没有得到期望的结果,报错:


ERROR TypeError: Cannot read properties of undefined (reading 'id')at ViewChildComp.set (ng-content.component.ts:57:27)at ViewChildComp_Query (template.html:3:5)at executeViewQueryFn (core.js:8758:5)at refreshView (core.js:7437:13)at refreshComponent (core.js:8527:13)at refreshChildComponents (core.js:7186:9)at refreshView (core.js:7430:13)at refreshComponent (core.js:8527:13)at refreshChildComponents (core.js:7186:9)at refreshView (core.js:7430:13)


问题分析

我们点击上图高亮的 template.html 调用栈:


来到我们自己 Component 的模板文件,因为这是一个内联到 Component 里的模板,所以显示为 template.html



通过单步调试,能发现在 refreshView 里,执行 view query 的入口逻辑:



function executeViewQueryFn(flags, viewQueryFn, component) {    ngDevMode && assertDefined(viewQueryFn, 'View queries function to execute must be defined.');    setCurrentQueryIndex(0);    viewQueryFn(flags, component);}
复制代码



根据关键字 view query 查询 Angular 官网,发现在 view query 里使用 id 选择器的正确语法,并不是直接查询 HTML 元素的 id 属性,而是需要在 HTML 元素或者 ng-template 里使用符号 # 指定一个 id,然后将这个 id 传入 @ViewChild




修改之后的运行效果:


总结

view query 在父 Component 需要访问其子 Component 的场景下特别有用。


假设有一个 alert Component:


@Component({  selector: 'alert',  template: `    <h1 (click)="alert()">{{type}}</h1>  `,})export class AlertComponent {  @Input() type: string = "success";    alert() {    console.log("alert");  }}
复制代码


在我们的父 Component 里,可以定义 AlertComponent 的多个实例:


@Component({  selector: 'my-app',  template: `    <alert></alert>    <alert type="danger"></alert>    <alert type="info"></alert>  `,})export class App {  @ViewChildren(AlertComponent) alerts: QueryList<AlertComponent>    ngAfterViewInit() {    this.alerts.forEach(alertInstance => console.log(alertInstance));  }}
复制代码


我们可以使用 @ViewChildren 装饰器从宿主视图中抓取元素。@ViewChildren 装饰器支持指令或组件类型作为参数,或模板变量的名称。当参数是组件/指令时,返回值将是组件/指令实例。


上面例子的 console.log 语句会打印 AlertComponent 的实例:



发布于: 刚刚阅读数: 3
用户头像

Jerry Wang

关注

🏆InfoQ写作平台-签约作者🏆 2017.12.03 加入

SAP成都研究院开发专家,SAP社区导师,SAP中国技术大使。2007 年从电子科技大学计算机专业硕士毕业后加入 SAP 成都研究院工作至今。工作中使用 ABAP, Java, JavaScript 和 TypeScript 进行开发。

评论

发布
暂无评论
关于 Angular view Query 的 id 选择器问题的单步调试_typescript_Jerry Wang_InfoQ写作社区