管道 Pipes
管道(Pipes)是 Angular 中用于转换模板显示数据的工具。Angular 提供了多种内置管道,也支持创建自定义管道。本章将详细介绍管道的使用方法。
内置管道
日期管道 date
import { Component } from '@angular/core';
import { DatePipe } from '@angular/common';
@Component({
selector: 'app-date-pipe',
template: `
<p>默认格式: {{ today | date }}</p>
<p>短日期: {{ today | date:'short' }}</p>
<p>长日期: {{ today | date:'long' }}</p>
<p>自定义格式: {{ today | date:'yyyy年MM月dd日 HH:mm:ss' }}</p>
<p>相对时间: {{ today | date:'medium' }}</p>
`,
imports: [DatePipe]
})
export class DatePipeComponent {
today = new Date();
}
常用日期格式:
| 格式 | 示例输出 |
|---|---|
short | 2024/1/15 14:30 |
medium | 2024年1月15日 14:30:00 |
long | 2024年1月15日 下午2:30:00 GMT+8 |
full | 2024年1月15日星期一 中国标准时间 下午2:30:00 |
shortDate | 2024/1/15 |
mediumDate | 2024年1月15日 |
longDate | 2024年1月15日 |
shortTime | 14:30 |
mediumTime | 14:30:00 |
大小写转换
import { Component } from '@angular/core';
import { UpperCasePipe, LowerCasePipe, TitleCasePipe } from '@angular/common';
@Component({
selector: 'app-case-pipe',
template: `
<p>原文: {{ text }}</p>
<p>大写: {{ text | uppercase }}</p>
<p>小写: {{ text | lowercase }}</p>
<p>标题: {{ text | titlecase }}</p>
`,
imports: [UpperCasePipe, LowerCasePipe, TitleCasePipe]
})
export class CasePipeComponent {
text = 'hello angular world';
}
数字格式化
import { Component } from '@angular/core';
import { DecimalPipe, PercentPipe, CurrencyPipe } from '@angular/common';
@Component({
selector: 'app-number-pipe',
template: `
<h4>数字格式化</h4>
<p>原始: {{ pi }}</p>
<p>保留2位小数: {{ pi | number:'1.0-2' }}</p>
<p>保留4位小数: {{ pi | number:'1.0-4' }}</p>
<h4>百分比</h4>
<p>默认: {{ ratio | percent }}</p>
<p>保留1位小数: {{ ratio | percent:'1.0-1' }}</p>
<h4>货币</h4>
<p>人民币: {{ price | currency:'CNY':'symbol':'1.0-2' }}</p>
<p>美元: {{ price | currency:'USD':'symbol':'1.0-2' }}</p>
<p>欧元: {{ price | currency:'EUR':'symbol':'1.0-2' }}</p>
`,
imports: [DecimalPipe, PercentPipe, CurrencyPipe]
})
export class NumberPipeComponent {
pi = 3.14159265;
ratio = 0.856;
price = 1234.56;
}
数字格式参数说明:'digitsInfo' 格式为 {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}
JSON 管道
import { Component } from '@angular/core';
import { JsonPipe } from '@angular/common';
@Component({
selector: 'app-json-pipe',
template: `
<pre>{{ user | json }}</pre>
`,
imports: [JsonPipe]
})
export class JsonPipeComponent {
user = {
name: '张三',
age: 25,
address: {
city: '北京',
district: '朝阳区'
}
};
}
切片管道 slice
import { Component } from '@angular/core';
import { SlicePipe } from '@angular/common';
@Component({
selector: 'app-slice-pipe',
template: `
<h4>字符串切片</h4>
<p>原文: {{ text }}</p>
<p>前5个字符: {{ text | slice:0:5 }}</p>
<p>后3个字符: {{ text | slice:-3 }}</p>
<h4>数组切片</h4>
<p>前3项: {{ items | slice:0:3 | json }}</p>
`,
imports: [SlicePipe, JsonPipe]
})
export class SlicePipeComponent {
text = 'Hello Angular World';
items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
}
异步管道 async
async 管道自动订阅 Observable 或 Promise,并在组件销毁时自动取消订阅:
import { Component } from '@angular/core';
import { AsyncPipe } from '@angular/common';
import { Observable, of } from 'rxjs';
import { delay } from 'rxjs/operators';
@Component({
selector: 'app-async-pipe',
template: `
<h4>Observable</h4>
<p>数据: {{ data$ | async }}</p>
<h4>Promise</h4>
<p>结果: {{ promise | async }}</p>
<h4>结合其他管道</h4>
<p>{{ user$ | async | json }}</p>
`,
imports: [AsyncPipe, JsonPipe]
})
export class AsyncPipeComponent {
data$: Observable<string> = of('异步数据').pipe(delay(1000));
promise: Promise<string> = new Promise(resolve => {
setTimeout(() => resolve('Promise 结果'), 500);
});
user$: Observable<{ name: string }> = of({ name: '张三' });
}
I18n 管道
国际化相关管道:
import { Component } from '@angular/core';
import { I18nPluralPipe, I18nSelectPipe } from '@angular/common';
@Component({
selector: 'app-i18n-pipe',
template: `
<h4>复数选择</h4>
<p>{{ minutes }} {{ minutes | i18nPlural:minuteMapping }}</p>
<h4>性别选择</h4>
<p>{{ gender | i18nSelect:genderMapping }}</p>
`,
imports: [I18nPluralPipe, I18nSelectPipe]
})
export class I18nPipeComponent {
minutes = 5;
minuteMapping: { [k: string]: string } = {
'=0': '零分钟',
'=1': '一分钟',
'other': '# 分钟'
};
gender = 'female';
genderMapping: { [k: string]: string } = {
'male': '他',
'female': '她',
'other': '它'
};
}
管道链
多个管道可以串联使用,从左到右依次执行:
import { Component } from '@angular/core';
import { UpperCasePipe, SlicePipe } from '@angular/common';
@Component({
selector: 'app-pipe-chain',
template: `
<!-- 先转大写,再切片 -->
<p>{{ text | uppercase | slice:0:10 }}</p>
<!-- 日期格式化后转大写 -->
<p>{{ today | date:'medium' | uppercase }}</p>
`,
imports: [UpperCasePipe, SlicePipe, DatePipe]
})
export class PipeChainComponent {
text = 'hello angular world';
today = new Date();
}
自定义管道
创建管道
使用 CLI 创建管道:
ng generate pipe truncate
手动创建管道:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'truncate',
standalone: true
})
export class TruncatePipe implements PipeTransform {
transform(value: string, limit: number = 20, suffix: string = '...'): string {
if (!value) return '';
if (value.length <= limit) return value;
return value.substring(0, limit) + suffix;
}
}
使用自定义管道:
import { Component } from '@angular/core';
import { TruncatePipe } from './truncate.pipe';
@Component({
selector: 'app-truncate-demo',
template: `
<p>原文: {{ longText }}</p>
<p>截断(默认): {{ longText | truncate }}</p>
<p>截断(30字符): {{ longText | truncate:30 }}</p>
<p>截断(30字符,自定义后缀): {{ longText | truncate:30:'…' }}</p>
`,
imports: [TruncatePipe]
})
export class TruncateDemoComponent {
longText = '这是一段很长的文字,用于演示截断管道的效果。通过自定义管道,我们可以灵活地处理文本显示。';
}
纯管道与非纯管道
默认情况下,管道是纯管道,只有在输入值变化时才会重新计算。对于引用类型(数组、对象),只有引用变化才会触发更新。
非纯管道会在每次变更检测时都重新计算:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'filter',
standalone: true,
pure: false // 非纯管道
})
export class FilterPipe implements PipeTransform {
transform(items: any[], filterFn: (item: any) => boolean): any[] {
if (!items || !filterFn) return items;
return items.filter(filterFn);
}
}
注意
非纯管道会影响性能,谨慎使用。对于数组过滤等场景,建议在组件中处理。
常用自定义管道示例
安全 HTML 管道
import { Pipe, PipeTransform, inject } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
@Pipe({
name: 'safeHtml',
standalone: true
})
export class SafeHtmlPipe implements PipeTransform {
private sanitizer = inject(DomSanitizer);
transform(value: string): SafeHtml {
return this.sanitizer.bypassSecurityTrustHtml(value);
}
}
使用:
@Component({
selector: 'app-safe-html',
template: `
<div [innerHTML]="htmlContent | safeHtml"></div>
`,
imports: [SafeHtmlPipe]
})
export class SafeHtmlComponent {
htmlContent = '<strong style="color: red;">安全的 HTML</strong>';
}
文件大小格式化
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'fileSize',
standalone: true
})
export class FileSizePipe implements PipeTransform {
transform(bytes: number): string {
if (bytes === 0) return '0 B';
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(1024));
return parseFloat((bytes / Math.pow(1024, i)).toFixed(2)) + ' ' + units[i];
}
}
使用:
@Component({
selector: 'app-file-size',
template: `
<p>{{ 1024 | fileSize }}</p> <!-- 1 KB -->
<p>{{ 1048576 | fileSize }}</p> <!-- 1 MB -->
<p>{{ 1073741824 | fileSize }}</p> <!-- 1 GB -->
`,
imports: [FileSizePipe]
})
export class FileSizeComponent {}
小结
- 内置管道提供了常用的数据转换功能
- 管道链可以串联多个管道依次处理数据
- 自定义管道通过
@Pipe装饰器创建,实现PipeTransform接口 - 纯管道只在输入值变化时重新计算,非纯管道每次变更检测都重新计算
- async 管道自动管理 Observable 和 Promise 的订阅
下一步
掌握了管道后,接下来学习 测试,了解如何编写单元测试和端到端测试。