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

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

问题描述

我有这样一个 Angular Component,模板文件如下:

@Component({
selector: ‘example-app’,
template: `
<pane id=“1” *ngIf=“shouldShow”>
<pane id=“2” *ngIf="!shouldShow">
<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 元素的实例。
```typescript
export 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 的实例:

相关文章
|
9月前
|
JavaScript
Angular 内容投影出现 No provider for TemplateRef found 错误的单步调试
Angular 内容投影出现 No provider for TemplateRef found 错误的单步调试
|
9月前
|
存储 JavaScript 前端开发
通过单步调试的方式学习 Angular 中 TView 和 LView 的概念
通过单步调试的方式学习 Angular 中 TView 和 LView 的概念
|
9月前
|
前端开发 JavaScript
通过单步调试的方式学习 Angular 中带有选择器的内容投影使用方式
通过单步调试的方式学习 Angular 中带有选择器的内容投影使用方式
|
9月前
|
存储
Angular 基于自定义指令的内容投影 content projection 问题的单步调试
Angular 基于自定义指令的内容投影 content projection 问题的单步调试
|
2月前
|
存储 前端开发 API
浅谈 Angular 应用前端消息显示机制的一个实际需求
浅谈 Angular 应用前端消息显示机制的一个实际需求
|
2月前
|
设计模式 JavaScript 前端开发
什么是 Angular 应用里的 Custom provider
什么是 Angular 应用里的 Custom provider
|
2月前
|
JavaScript 前端开发 架构师
Angular进阶:理解RxJS在Angular应用中的高效运用
RxJS(Reactive Extensions for JavaScript)是JavaScript的一个响应式编程库,特别适用于处理异步数据流。
35 0
|
2月前
|
JavaScript 前端开发
Angular.js 应用中数据模式的删除操作实现
Angular.js 应用中数据模式的删除操作实现
|
2月前
|
存储 JavaScript 前端开发
Angular 应用 node_modules 子文件夹 @types 的作用介绍
Angular 应用 node_modules 子文件夹 @types 的作用介绍
|
7天前
|
JavaScript 前端开发 开发者
Angular框架:企业级Web应用的强大后盾
Angular,谷歌支持的JavaScript框架,因其组件化架构、双向数据绑定、依赖注入和路由系统,成为企业级Web开发首选。组件化促进代码重用,如`AppComponent`示例。双向数据绑定简化DOM操作,减少手动工作。依赖注入通过示例展示易管理依赖,提升测试性。路由则支持SPA开发,平滑页面过渡。Angular的特性增强了开发效率和应用质量,使其在Web开发领域保持领先地位。【6月更文挑战第25天】
15 2