美文网首页react & vue & angular收藏
关于 Angular view Query 的 id 选择器问题

关于 Angular view Query 的 id 选择器问题

作者: _扫地僧_ | 来源:发表于2022-03-18 09:25 被阅读0次

    问题描述

    我有这样一个 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 元素的实例。
    
    ```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 的实例:


    相关文章

      网友评论

        本文标题:关于 Angular view Query 的 id 选择器问题

        本文链接:https://www.haomeiwen.com/subject/tdaurrtx.html