模板引用变量是模板中某一元素的引用,这个元素可以是标准的 HTML 元素,可以是 Angular 组件,也可以是 ng-template 元素(angular 的结构型指令,常见于UI框架),甚至也可以是完全自定义的元素(使用较少)。
定义模板引用变量
# + 变量名称
例子:
<input #phone placeholder="phone number" />
使用模板引用变量来引用 HTML 元素,在实际开发中是十分常见的。
例子:
// html <section> <input #person placeholder="" type="text" /> <button (click)="showName(person.value)">Search</button> </section> // ts showName(name:string){ console.log(name); }
通过模板引用变量,我们可以很轻松地获得组件内部的变量和方法。
例子:
// 子组件:my-component.component.html <p> hello {{ name }} ! </p> // 子组件:my-component.component.ts name:string = 'Tom'; welcome:string = 'Welcome to China, nice to meet you!'; changeName(){ this.name = 'Amy'; } // html // 引用子组件 my-component.component <section> <app-my-component #person></app-my-component> <p>{{ person.welcome }}</p> <button (click)="person.changeName()">Change</button> </section>
ng-template 是 Angular 的一个结构型指令,用于定义内嵌模板。但其定义的模板不会直接显示出来,而是需要通过 ngif 等指令或者通过 ViewContainerRef 的 createEmbeddedView() 方法将内容加载到页面上。
ng-template 与 ngif 指令
例子:
// html <section> <p *ngIf="expression else myTemplate">Welcome to China</p> <ng-template #myTemplate> <p>Welcome to Korea</p> </ng-template> </section> // ts expression = false;
例子中,如果 expression 为 true,则展示“Welcome to China”,否则展示“Welcome to Korea”。
ng-template 与 ViewContainerRef 接口
例子:
// app.component.html <section> <button (click)="showMyTemplate()">show</button> <ng-template #myTemplate> <p>Welcome to Korea</p> </ng-template> </section> // app.component.ts // 引入 ViewChild、TemplateRef、ViewContainerRef // ViewChild: 装饰器,Angular 提供的元素查询工具,可以使用它在模板中查询模板引用变量 // TemplateRef: Angular 提供的内嵌模板的接口,指向 <ng-template> 元素 // ViewContainerRef: Angular 提供的用于创建和管理内嵌模板的接口 import { Component, OnInit, ViewChild, TemplateRef, ViewContainerRef } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent implements OnInit { // 取到变量 myTemplate,并实现 TemplateRef 接口 @ViewChild('myTemplate') myRef!: TemplateRef<any>; constructor( // 初始化 ViewContainerRef 接口 private viewRef : ViewContainerRef ) { } ngOnInit() { } showMyTemplate(){ // 调用 ViewContainerRef 中的 createEmbeddedView() 方法 // 将内嵌模板的内容加载到页面上 this.viewRef.createEmbeddedView(this.myRef); } }
说到模板引用变量的作用域,我们就不得不重提一下结构型指令,在 Angular 中,* 符号意味着简写:
*ngIf="exp" *ngFor="let item of [1,2,3]"
实际上,非简写的形式是围绕 ng-template 元素进行的,只是由简写转换为普通语法的过程,Angular 已经帮助我们去完成了:
<ng-template [ngIf]="exp"> <ng-template ngFor let-item [ngForOf]="[1,2,3]">
因此,在模板变量的作用域的定义中,提到了不可以在模板的外部访问该模板内部的模板变量,而找到这个内部和外部的边界,就显得尤为重要,通常可以将结构型指令(ngIf、ngFor、ng-template)当作这个边界。
例子:
// html <section> <input *ngIf="true" type="text" [(ngModel)]="name" #person> <p>{{ person.value }}</p> </section> // ts name:string = '';
如果现在无法判断是否可以访问到模板变量 person,我们可以恢复一下例子的非简写模式:
例子:
// html <section> <ng-template [ngIf]="true"> <input type="text" [(ngModel)]="name" #person> </ng-template> <p>{{ person.value }}</p> </section> // ts name:string = '';
此时可以看出来,例子中,我们是在 ng-template 模板的外部(p 元素处)访问其内部的变量 person,因此是访问不到的,会报错。
再看下面的例子:
例子:
// html <section> <input type="text" [(ngModel)]="name" #person> <p *ngIf="true">{{ person.value }}</p> </section> // ts name:string = '';
将例子恢复成非简写模式:
例子:
// html <section> <input type="text" [(ngModel)]="name" #person> <ng-template [ngIf]="true"> <p>{{ person.value }}</p> </ng-template> </section> // ts name:string = '';
此时,我们是在 ng-template 模板的内部访问其外部(或者叫父辈)的变量 person,根据 ES 的查询机制,这样是可行的,因此,可以访问到变量 person。
end