跳到主要内容

模板语法

Angular 模板基于 HTML,扩展了额外的语法来绑定数据、处理事件和控制视图结构。本章将详细介绍 Angular 模板的各种语法特性。

数据绑定

插值表达式

使用双花括号 {{ }} 显示组件属性值:

@Component({
selector: 'app-greeting',
template: `
<h1>{{ title }}</h1>
<p>欢迎,{{ user.name }}!</p>
<p>年龄: {{ user.age }} 岁</p>
<p>计算结果: {{ 1 + 1 }}</p>
<p>调用方法: {{ getFullName() }}</p>
`
})
export class GreetingComponent {
title = 'Angular 教程';
user = { name: '张三', age: 25 };

getFullName() {
return `${this.user.name} (ID: 1001)`;
}
}

插值表达式支持:

  • 组件属性访问
  • 算术运算
  • 方法调用
  • 表达式求值

属性绑定

使用方括号 [ ] 绑定 HTML 属性或组件输入:

@Component({
selector: 'app-property-binding',
template: `
<!-- 绑定 HTML 属性 -->
<img [src]="imageUrl" [alt]="imageAlt">
<a [href]="link" [target]="target">链接</a>
<button [disabled]="isDisabled">提交</button>

<!-- 绑定 class -->
<div [class.active]="isActive">激活状态</div>
<div [class]="{ 'text-bold': isBold, 'text-red': isRed }">多类名</div>

<!-- 绑定 style -->
<div [style.color]="textColor">文字颜色</div>
<div [style.font-size.px]="fontSize">字体大小</div>
`
})
export class PropertyBindingComponent {
imageUrl = 'https://example.com/image.png';
imageAlt = '示例图片';
link = 'https://angular.dev';
target = '_blank';
isDisabled = false;
isActive = true;
isBold = true;
isRed = false;
textColor = 'red';
fontSize = 16;
}

事件绑定

使用圆括号 ( ) 绑定事件处理器:

@Component({
selector: 'app-event-binding',
template: `
<!-- 点击事件 -->
<button (click)="onClick()">点击我</button>

<!-- 传递事件对象 -->
<button (click)="onClickWithEvent($event)">获取事件</button>

<!-- 输入事件 -->
<input (input)="onInput($event)" placeholder="输入内容">

<!-- 键盘事件 -->
<input (keyup.enter)="onEnter()" placeholder="按回车">
<input (keyup.escape)="onEscape()" placeholder="按 ESC">

<!-- 表单事件 -->
<form (submit)="onSubmit($event)">
<button type="submit">提交</button>
</form>
`
})
export class EventBindingComponent {
onClick() {
console.log('按钮被点击');
}

onClickWithEvent(event: MouseEvent) {
console.log('事件类型:', event.type);
console.log('目标元素:', event.target);
}

onInput(event: Event) {
const value = (event.target as HTMLInputElement).value;
console.log('输入值:', value);
}

onEnter() {
console.log('按下回车键');
}

onEscape() {
console.log('按下 ESC 键');
}

onSubmit(event: Event) {
event.preventDefault();
console.log('表单提交');
}
}

双向绑定

使用 [(ngModel)] 实现双向数据绑定:

import { FormsModule } from '@angular/forms';

@Component({
selector: 'app-two-way',
template: `
<input [(ngModel)]="name" placeholder="输入名字">
<p>你输入的是: {{ name }}</p>

<input type="checkbox" [(ngModel)]="isChecked">
<p>复选框状态: {{ isChecked }}</p>

<select [(ngModel)]="selectedCity">
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="guangzhou">广州</option>
</select>
<p>选择的城市: {{ selectedCity }}</p>
`,
imports: [FormsModule]
})
export class TwoWayBindingComponent {
name = '';
isChecked = false;
selectedCity = 'beijing';
}

新控制流语法

Angular 17 引入了新的内置控制流语法,更简洁高效。

条件渲染 @if

@Component({
selector: 'app-conditional',
template: `
<!-- 基本条件 -->
@if (isLoggedIn) {
<p>欢迎回来,{{ userName }}!</p>
}

<!-- if-else -->
@if (score >= 60) {
<p>及格</p>
} @else {
<p>不及格</p>
}

<!-- if-else if-else -->
@if (score >= 90) {
<p>优秀</p>
} @else if (score >= 80) {
<p>良好</p>
} @else if (score >= 60) {
<p>及格</p>
} @else {
<p>不及格</p>
}

<!-- 带条件存储 -->
@if (user; as u) {
<p>用户名: {{ u.name }}</p>
}
`
})
export class ConditionalComponent {
isLoggedIn = true;
userName = '张三';
score = 85;
user = { name: '李四', age: 30 };
}

循环渲染 @for

@Component({
selector: 'app-loop',
template: `
<!-- 基本循环 -->
<ul>
@for (item of items; track item.id) {
<li>{{ item.name }}</li>
}
</ul>

<!-- 带索引 -->
<ol>
@for (item of items; track item.id; let i = $index) {
<li>{{ i + 1 }}. {{ item.name }}</li>
}
</ol>

<!-- 完整上下文变量 -->
@for (item of items; track item.id; let i = $index, first = $first, last = $last, even = $even, odd = $odd) {
<div [class.first]="first" [class.last]="last">
索引: {{ i }}, 名称: {{ item.name }}
<span *ngIf="first">(第一个)</span>
<span *ngIf="last">(最后一个)</span>
</div>
}

<!-- 空列表处理 -->
@for (item of emptyList; track item) {
<p>{{ item }}</p>
} @empty {
<p>列表为空</p>
}
`
})
export class LoopComponent {
items = [
{ id: 1, name: '苹果' },
{ id: 2, name: '香蕉' },
{ id: 3, name: '橙子' }
];
emptyList: string[] = [];
}

@for 的上下文变量:

变量说明
$index当前索引(从 0 开始)
$first是否为第一个元素
$last是否为最后一个元素
$even索引是否为偶数
$odd索引是否为奇数
$count列表总长度

开关语句 @switch

@Component({
selector: 'app-switch',
template: `
@switch (status) {
@case ('loading') {
<p>加载中...</p>
}
@case ('success') {
<p>操作成功!</p>
}
@case ('error') {
<p>发生错误</p>
}
@default {
<p>未知状态</p>
}
}
`
})
export class SwitchComponent {
status = 'loading';
}

模板引用变量

使用 # 定义模板引用变量,获取 DOM 元素或组件实例:

@Component({
selector: 'app-template-ref',
template: `
<!-- 引用输入框 -->
<input #nameInput type="text" placeholder="输入名字">
<button (click)="focusInput(nameInput)">聚焦</button>
<p>输入值: {{ nameInput.value }}</p>

<!-- 引用组件 -->
<app-child #child></app-child>
<button (click)="child.doSomething()">调用子组件方法</button>

<!-- 配合 ngModel -->
<input #nameModel="ngModel" [(ngModel)]="name">
<p *ngIf="nameModel.invalid && nameModel.touched">
请输入有效的名字
</p>
`
})
export class TemplateRefComponent {
name = '';

focusInput(input: HTMLInputElement) {
input.focus();
}
}

安全导航操作符

使用 ?. 安全地访问可能为空的属性:

@Component({
selector: 'app-safe-nav',
template: `
<!-- 如果 user 为 null,不会报错,显示空 -->
<p>用户名: {{ user?.name }}</p>
<p>城市: {{ user?.address?.city }}</p>

<!-- 等价于 -->
<p>用户名: {{ user && user.name }}</p>
`
})
export class SafeNavComponent {
user: { name: string; address: { city: string } } | null = null;
}

管道

管道用于转换模板中显示的数据:

@Component({
selector: 'app-pipes',
template: `
<!-- 内置管道 -->
<p>大写: {{ name | uppercase }}</p>
<p>小写: {{ name | lowercase }}</p>
<p>标题: {{ name | titlecase }}</p>
<p>日期: {{ today | date }}</p>
<p>自定义格式: {{ today | date:'yyyy-MM-dd HH:mm:ss' }}</p>
<p>货币: {{ price | currency:'CNY':'symbol':'1.0-2' }}</p>
<p>百分比: {{ ratio | percent:'1.0-2' }}</p>
<p>数字: {{ pi | number:'1.0-4' }}</p>

<!-- 链式管道 -->
<p>{{ name | uppercase | slice:0:5 }}</p>

<!-- 带参数的管道 -->
<p>{{ items | json }}</p>
`
})
export class PipesComponent {
name = 'hello angular';
today = new Date();
price = 1234.56;
ratio = 0.856;
pi = 3.14159265;
items = ['a', 'b', 'c'];
}

常用内置管道

管道说明示例
uppercase转大写{{ 'abc' | uppercase }}
lowercase转小写{{ 'ABC' | lowercase }}
titlecase首字母大写{{ 'hello world' | titlecase }}
date日期格式化{{ date | date:'yyyy-MM-dd' }}
currency货币格式化{{ price | currency:'CNY' }}
percent百分比格式化{{ 0.85 | percent }}
number数字格式化{{ pi | number:'1.0-2' }}
jsonJSON 字符串化{{ obj | json }}
slice数组/字符串切片{{ arr | slice:0:3 }}
async异步数据{{ promise$ | async }}

模板中的类型转换

使用 $any() 禁用类型检查:

@Component({
selector: 'app-type-cast',
template: `
<!-- 禁用类型检查 -->
<p>{{ $any(this).someProperty }}</p>
`
})

小结

  1. 数据绑定包括插值、属性绑定、事件绑定和双向绑定
  2. 新控制流语法 @if@for@switch 更简洁高效
  3. 模板引用变量使用 # 获取元素或组件引用
  4. 管道用于转换显示数据,支持链式调用

下一步

掌握了模板语法后,接下来学习 Signals 响应式,了解 Angular 全新的响应式状态管理方式。