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,默认在变更检测之后才会获取到元素,所以
constructor
和ngOnInit
中的console
打印为空,而ngAfterViewInit
就是在变更检测之后才调用的
- static
如果一个元素是静态的,你又想尽早得拿到该元素,可以设置它的
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);
}
}
- 获取子组件(指令) 写法2
也可以通过模版引用变量获取子组件
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属性
- 批量获取子组件和dom元素
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);
}
}
- QueryList
模板元素集合
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);
});
}
}