基本概念
Angular 内置了 ViewChild 和 ViewChildren 装饰器来获取模板中匹配到的元素或指令。
在 ngAfterViewInit 钩子函数中才能够查看获取到的元素或指令。
*ngAfterViewInit:组件视图初始化之后调用。
ViewChild
获取模板中匹配到的第一个元素或指令。
1、获取原生 DOM 元素
通过模板引用变量来获取原生 DOM 元素。
例子:
// app.component.html
<section>
<p #text>hello</p>
</section>
// app.component.ts
// 引入装饰器 ViewChild 和接口 ElementRef
import { Component, AfterViewInit, ViewChild, ElementRef} from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements AfterViewInit {
// 取到变量 text,类型为 ElementRef
@ViewChild('text')
txt!:ElementRef;
constructor() { }
// 查看获取到的元素
ngAfterViewInit(){
console.log(this.txt.nativeElement.innerHTML);
}
}
ElementRef 是 Angular 封装的一个用于操作原生 DOM 元素的接口,究其原因,是因为 Angular 本身不局限于只在浏览器上运行(比如还有 web workder 环境,不存在 DOM)。因此,Angular 内置了 ElementRef 隔离开应用层与 DOM 层。如果我们需要直接操作 DOM 元素,可以通过 ElementRef 下的 nativeElement 属性。
2、获取组件及其内部属性
获取组件有两种方式,第一种依然是通过模板引用变量:
例子:
// app.component.html
<section>
<app-my-component #text></app-my-component>
</section>
// app.component.ts
/// 引入装饰器 ViewChild 和子组件 MyComponentComponent
import { Component, AfterViewInit, ViewChild} from '@angular/core';
import { MyComponentComponent } from './components/my-component/my-component.component';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements AfterViewInit {
// 取到变量 text,类型设置为 MyComponentComponent
@ViewChild('text')
txt!:MyComponentComponent; // 类型为 any 也可以
constructor( ) { }
// 查看获取到的元素
// welcome 为子组件的属性,可以直接使用
ngAfterViewInit(){
console.log(this.txt.welcome);
}
}
第二种是通过组件类:
// 如果不使用模板引用变量
// 直接引入组件 MyComponentComponent 也可以
// 如下写法:
@ViewChild(MyComponentComponent)
txt!:MyComponentComponent;
3、获取指令
通过模板引用变量来获取 ng-template 指令的内容在《模板引用变量》已经详细介绍过,在此就不再赘述。
设置查询条件
ViewChild 可以选择性设置第二个参数 read,用来增加查询条件。但这个参数不是必需的,因为 Angular 会根据即将返回的类型自动添加查询条件,比如将要返回的是原生 DOM 元素,那么查询类型就是 ElementRef,如果将要返回的是内嵌模板 ng-template,那么查询类型就 TemplateRef,再比如我们上个例子中,将要返回的是子组件将
MyComponentComponent
,那么默认的查询类型就是MyComponentComponent
。
例子:
// html
<section>
<app-my-component #text></app-my-component>
<ng-template #name>
<p>Tom</p>
</ng-template>
<p #age>20</p>
</section>
// ts
import { Component, AfterViewInit, ViewChild, ElementRef, TemplateRef } from '@angular/core';
@ViewChild('text',{read: MyComponentComponent})
txt!:MyComponentComponent;
@ViewChild('name',{read:TemplateRef})
name!:TemplateRef<any>;
@ViewChild('age',{read:ElementRef})
age!:ElementRef;
ngAfterViewInit(){
console.log(this.txt);
console.log(this.name);
console.log(this.age);
}
上面例子中,不添加查询条件,返回的结果是一样的:
例子:
@ViewChild('text')
txt!:MyComponentComponent;
@ViewChild('name')
name!:TemplateRef<any>;
@ViewChild('age')
age!:ElementRef;
但是,ViewContainerRef 类型不能被 Angular 推断出来,需要手动添加查询条件。
例子:
// html
<section>
<ng-template #name>
<p>Tom</p>
</ng-template>
<p #age>20</p>
</section>
// ts
import { Component, AfterViewInit, ViewChild, ElementRef, TemplateRef ,ViewContainerRef} from '@angular/core';
@ViewChild('name')
name!:TemplateRef<any>;
@ViewChild('name',{read:ViewContainerRef})
nameActive!:ViewContainerRef;
@ViewChild('age',{read:ViewContainerRef})
age!:ViewContainerRef;
ngAfterViewInit(){
this.nameActive.createEmbeddedView(this.name);
this.age.createEmbeddedView(this.name);
}
ViewChildren
获取模板中匹配到的多个元素或指令,返回一个 QueryList 集合。
例子:
// html
<section>
<app-my-component #text></app-my-component>
<ng-template #text>
<p>Tom</p>
</ng-template>
<p #text>20</p>
</section>
// ts
import { Component, AfterViewInit, ViewChildren, QueryList} from '@angular/core';
@ViewChildren('text')
txt!:QueryList<any>;
ngAfterViewInit(){
if (this.txt != null && this.txt.length !== 0) {
this.txt.forEach(el => console.log(el));
}
}
end