跳到主要内容

管道 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();
}

常用日期格式:

格式示例输出
short2024/1/15 14:30
medium2024年1月15日 14:30:00
long2024年1月15日 下午2:30:00 GMT+8
full2024年1月15日星期一 中国标准时间 下午2:30:00
shortDate2024/1/15
mediumDate2024年1月15日
longDate2024年1月15日
shortTime14:30
mediumTime14: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 {}

小结

  1. 内置管道提供了常用的数据转换功能
  2. 管道链可以串联多个管道依次处理数据
  3. 自定义管道通过 @Pipe 装饰器创建,实现 PipeTransform 接口
  4. 纯管道只在输入值变化时重新计算,非纯管道每次变更检测都重新计算
  5. async 管道自动管理 Observable 和 Promise 的订阅

下一步

掌握了管道后,接下来学习 测试,了解如何编写单元测试和端到端测试。