Angular13 Angular 模板引用变量

2024-02-25 开发教程 Angular13 匿名 4

模板变量

模板变量可以帮助你在模板的另一部分使用这个部分的数据。使用模板变量,你可以执行某些任务,比如响应用户输入或微调应用的表单。

模板变量可以引用这些东西:

  • 模板中的 DOM 元素
  • 指令
  • 元素
  • TemplateRef
  • Web 组件

本章包含代码片段的工作实例参阅现场演练/ 下载范例

语法

在模板中,要使用井号 ​#​ 来声明一个模板变量。下列模板变量 ​#phone​ 语法在 ​<input>​ 元素上声明了一个名为 ​phone ​的变量

<input #phone placeholder="phone number" />

可以在组件模板中的任何地方引用某个模板变量。这里的 ​<button>​ 就引用了 ​phone ​变量。

<input #phone placeholder="phone number" />
<!-- lots of other elements -->
<!-- phone refers to the input element; pass its `value` to an event handler -->
<button (click)="callPhone(phone.value)">Call</button>

Angular 是如何为模板变量赋值的

Angular 根据你所声明的变量的位置给模板变量赋值:

  • 如果在组件上声明变量,该变量就会引用该组件实例。
  • 如果在标准的 HTML 标记上声明变量,该变量就会引用该元素。
  • 如果你在 ​<ng-template>​ 元素上声明变量,该变量就会引用一个 ​TemplateRef ​实例来代表此模板。
  • 如果该变量在右侧指定了一个名字,比如 ​#var="ngModel"​ ,那么该变量就会引用所在元素上具有这个 ​exportAs ​名字的指令或组件。

将 NgForm 与模板变量一起使用

在大多数情况下,Angular 会把模板变量的值设置为它所在的元素。在前面的例子中, ​phone ​引用的是电话号码 ​<input>​ 。该按钮的 click 处理程序会把这个 ​<input>​ 的值传给该组件的 ​callPhone()​ 方法。

这里的 ​NgForm ​指令演示了如何通过引用指令的的 ​exportAs ​名字来引用不同的值。在下面的例子中,模板变量 ​itemForm ​在 HTML 中分别出现了三次。

<form #itemForm="ngForm" (ngSubmit)="onSubmit(itemForm)">
<label for="name">Name</label>
<input type="text" id="name" class="form-control" name="name" ngModel required />
<button type="submit">Submit</button>
</form>
<div [hidden]="!itemForm.form.valid">
<p>{{ submitMessage }}</p>
</div>

如果没有 ​ngForm ​这个属性值,​itemForm ​引用的值将是 HTMLFormElement 也就是 ​<form>​ 元素。而 ​Component ​和 ​Directive ​之间的差异在于 Angular 在没有指定属性值的情况下,Angular 会引用 ​Component​,而 ​Directive ​不会改变这种隐式引用(即它的宿主元素)。

而使用了 ​NgForm ​之后,​itemForm ​就是对 ​NgForm ​指令的引用,可以用它来跟踪表单中每一个控件的值和有效性。

与原生的 ​<form>​ 元素不同, ​NgForm ​指令有一个 ​form ​属性。如果 ​itemForm.form.valid​ 无效,那么 ​NgForm ​的 ​form ​属性就会让你禁用提交按钮。

模板变量的作用域

可以在包含此模板变量的模板中的任何地方引用它。而 结构型指令(如 ​*ngIf​ 和 ​*ngFor​ 或 ​<ng-template>​ 同样充当了模板的边界。你不能在这些边界之外访问其中的模板变量。

同名变量在模板中只能定义一次,这样运行时它的值就是可预测的。

在嵌套模板中访问

内部模板可以访问外模板定义的模板变量。

在下面的例子中,修改 ​<input>​ 中的文本值也会改变 ​<span>​ 中的值,因为 Angular 会立即通过模板变量 ​ref1 ​来更新这种变化。

<input #ref1 type="text" [(ngModel)]="firstExample" />
<span *ngIf="true">Value: {{ ref1.value }}</span>

在这种情况下,有一个包含这个 ​<span>​ 的隐式 ​<ng-template>​,而该变量的定义在该隐式模板之外。访问父模板中的模板变量是可行的,因为子模板会从父模板继承上下文。

我们用更啰嗦的形式重写上述的代码,可以明确地显示出 ​<ng-template>​。

<input #ref1 type="text" [(ngModel)]="firstExample" />
<!-- New template -->
<ng-template [ngIf]="true">
<!-- Because the context is inherited, the value is available to the new template -->
<span>Value: {{ ref1.value }}</span>
</ng-template>

但是,从外部的父模板访问本模板中的变量是行不通的。

<input *ngIf="true" #ref2 type="text" [(ngModel)]="secondExample" />
<span>Value: {{ ref2?.value }}</span> <!-- doesn't work -->

这个更啰嗦的形式表明 ​ref2 ​位于外部的父模板中。

<ng-template [ngIf]="true">
<!-- The reference is defined within a template -->
<input #ref2 type="text" [(ngModel)]="secondExample" />
</ng-template>
<!-- ref2 accessed from outside that template doesn't work -->
<span>Value: {{ ref2?.value }}</span>

考虑下面这个带 ​*ngFor​ 的使用范例。

<ng-container *ngFor="let i of [1,2]">
<input #ref type="text" [value]="i" />
</ng-container>
{{ ref.value }}

这里,​ref.value​ 不起作用。结构型指令 ​*ngFor​ 将模板实例化了两次,因为 ​*ngFor​ 在对数组中的两个条目进行迭代。因此不可能定义出 ​ref.value​ 指向的是谁。

对于结构型指令,比如 ​*ngFor​ 或 ​*ngIf​ ,Angular 也无法知道模板是否曾被实例化过。

结果,Angular 无法访问该值并返回错误。

访问 <ng-template> 的模板变量

在 ​<ng-template>​ 上声明变量时,该变量会引用一个 ​TemplateRef ​实例来表示该模板。

<ng-template #ref3></ng-template>
<button (click)="log(ref3)">Log type of #ref</button>

在这个例子中,单击该按钮会调用 ​log()​ 函数,它把 ​#ref3​ 的值输出到控制台。因为 ​#ref ​变量在 ​<ng-template>​ 上,所以它的值是一个 ​TemplateRef​。

下面是一个名为 ​TemplateRef ​的 ​TemplateRef()​ 函数在浏览器控制台中展开时的输出。

▼ ƒ TemplateRef()
name: "TemplateRef"
__proto__: Function

模板输入变量

模板输入变量是可以在模板的单个实例中引用的变量。你可以用 ​let ​关键字声明模板输入变量,比如 ​let hero​。

在这个例子中,有几个这样的变量:​hero​、​i​ 和 ​odd​。

<ng-template #hero let-hero let-i="index" let-odd="isOdd">
<div [class]="{'odd-row': odd}">{{i}}:{{hero.name}}</div>
</ng-template>

此变量的范围仅限于可复写模板中的单个实例。可以在其他结构型指令的定义中再次使用相同的变量名。

相反,你可以通过在变量名称前加上 ​#​ 来声明模板变量,如 ​#var​。模板变量引用其附加的元素、组件或指令。

模板输入变量和模板变量名称具有各自的名称空间。​let hero​ 中的模板输入变量 ​hero ​和 ​#hero​ 中的模板变量 ​hero ​是不同的。