14Angular ViewChild和ViewChildren

2020-12-26  本文已影响0人  learninginto
ViewChild

用于获取组件模板上的元素

import {AfterViewInit, Component, ElementRef, OnInit, ViewChild} from '@angular/core';

@Component({
  selector: 'app-view-child',
  template: `
      <section>
        <h3>获取dom</h3>
        <div class="box" #box>
          <p>box</p>
        </div>
      </section>
 `,
  styles: []
})
export class ViewChildComponent implements OnInit, AfterViewInit {
  @ViewChild('box') private boxEl: ElementRef;
  constructor() {
    // TypeError: Cannot read property 'nativeElement' of undefined
    console.log('0', this.boxEl.nativeElement);
  }

  ngOnInit(): void {
    // TypeError: Cannot read property 'nativeElement' of undefined
    console.log('1', this.boxEl.nativeElement);
  }
  ngAfterViewInit(): void {
    console.log('2', this.boxEl.nativeElement); // 正确
  }
}

上面例子中的boxEl,默认在变更检测之后才会获取到元素,所以constructorngOnInit中的console打印为空,而ngAfterViewInit就是在变更检测之后才调用的

如果一个元素是静态的,你又想尽早得拿到该元素,可以设置它的static属性为true

export class ViewChildComponent implements OnInit, AfterViewInit {
  @ViewChild('box', { static: true }) private boxEl: ElementRef;
  constructor() {
    // TypeError: Cannot read property 'nativeElement' of undefined
    console.log('0', this.boxEl.nativeElement);
  }

  ngOnInit(): void {
    console.log('1', this.boxEl.nativeElement); // 正确
  }
  ngAfterViewInit(): void {
    console.log(2, this.boxEl.nativeElement); // 正确
  }
}

建议如果目标从一开始就显示在模版上,即没有被ngIf等指令操控时,就开启static:true

获取到组件实例后可以访问子组件到属性和方法

import {AfterViewInit, Component, ElementRef, OnInit, ViewChild} from '@angular/core';
@Component({
  selector: 'app-view-child-panel',
  templateUrl: './view-child-panel.component.html'
})
export class ViewChildPanelComponent implements OnInit {
  readonly name = 'panel';
  constructor() { }
  ngOnInit(): void {}
}


@Component({
  selector: 'app-view-child',
  template: `
      <section>
        <h3>获取自组件</h3>
        <app-view-child-panel></app-view-child-panel>
      </section>
 `,
  styles: []
})
export class ViewChildComponent implements OnInit, AfterViewInit {
  @ViewChild(ViewChildPanelComponent, { static: true }) private panel: ViewChildPanelComponent;
    constructor() {}
    ngOnInit(): void {}
    ngAfterViewInit(): void {
      // console.log(2, this.boxEl.nativeElement);
      console.log(this.panel.name);
    }
}

也可以通过模版引用变量获取子组件

import {AfterViewInit, Component, ElementRef, OnInit, ViewChild} from '@angular/core';

@Component({
  selector: 'app-view-child',
  template: `
      <section>
        <h3>获取自组件</h3>
        <app-view-child-panel #myPanel></app-view-child-panel>
      </section>
 `,
  styles: []
})
export class ViewChildComponent implements OnInit, AfterViewInit {
  @ViewChild('myPanel', { read: ViewChildPanelComponent, static: true }) private panel: ViewChildPanelComponent;
    constructor() {}
    ngOnInit(): void {}
    ngAfterViewInit(): void {
      // console.log(2, this.boxEl.nativeElement);
      console.log(this.panel.name);
    }
}
ViewChildren

与ViewChild类似,不同的是,它可以批量获取模板上相同选择器的元素,并存放到QueryList类中。

注意:ViewChildren没有static属性

import {AfterViewInit, Component, ElementRef, OnInit, ViewChild} from '@angular/core';

@Component({
  selector: 'app-view-child',
  template: `
      <section>
        <h3>获取dom</h3>
        <div class="box" #box>
          <p>box</p>
        </div>
      </section>
      
      <section #box>
        <h3>获取子组件</h3>
        <app-view-child-panel #myPanel></app-view-child-panel>
        <app-view-child-panel #myPanel></app-view-child-panel>
        <app-view-child-panel #myPanel></app-view-child-panel>
      </section>
 `,
  styles: []
})
export class ViewChildComponent implements OnInit, AfterViewInit {
  @ViewChild('box', { static: true }) private boxEl: ElementRef;
  @ViewChildren('box') private boxEls: QueryList<ElementRef>;
  @ViewChild(ViewChildPanelComponent, { static: true }) private panel: ViewChildPanelComponent;
  @ViewChildren(ViewChildPanelComponent) private panels: QueryList<ViewChildPanelComponent>;    
    constructor() {}
    ngOnInit(): void {}
    ngAfterViewInit(): void {
      console.log(this.panels);
      console.log(this.boxEls);
    }
}

模板元素集合

import {AfterViewInit, Component, ElementRef, OnInit, ViewChild} from '@angular/core';

@Component({
  selector: 'app-view-child',
  template: `
      
      <section>
        <h3>获取子组件</h3>
        <button class="btn btn-primary" (click)="showMidPanel = !showMidPanel">toggle mid</button>
        <app-view-child-panel #myPanel></app-view-child-panel>
        <app-view-child-panel #myPanel *ngIf="showMidPanel"></app-view-child-panel>
        <app-view-child-panel #myPanel></app-view-child-panel>
      </section>
 `,
  styles: []
})
export class ViewChildComponent implements OnInit, AfterViewInit {
  @ViewChildren(ViewChildPanelComponent) private panels: QueryList<ViewChildPanelComponent>;    constructor() {}
    ngOnInit(): void {}
    ngAfterViewInit(): void {
      this.panels.changes.subscribe(changes => {
        console.log('changes', changes);
      });
    }
}
上一篇下一篇

猜你喜欢

热点阅读