管道 #

一、管道概述 #

管道用于在模板中转换数据,将数据格式化为用户友好的形式。

html
<p>{{ birthday | date }}</p>
<p>{{ price | currency }}</p>
<p>{{ name | uppercase }}</p>

二、内置管道 #

2.1 日期管道 #

html
<p>{{ birthday | date }}</p>
<p>{{ birthday | date:'short' }}</p>
<p>{{ birthday | date:'long' }}</p>
<p>{{ birthday | date:'fullDate' }}</p>
<p>{{ birthday | date:'yyyy-MM-dd' }}</p>
<p>{{ birthday | date:'MM/dd/yyyy' }}</p>
<p>{{ birthday | date:'h:mm a' }}</p>

2.2 数字管道 #

html
<p>{{ 1234.56 | number }}</p>
<p>{{ 1234.56 | number:'1.0-0' }}</p>
<p>{{ 1234.56 | number:'1.2-2' }}</p>
<p>{{ 0.259 | percent }}</p>
<p>{{ 0.259 | percent:'1.0-2' }}</p>

2.3 货币管道 #

html
<p>{{ 1234.56 | currency }}</p>
<p>{{ 1234.56 | currency:'CNY' }}</p>
<p>{{ 1234.56 | currency:'USD':'symbol' }}</p>
<p>{{ 1234.56 | currency:'CNY':'symbol':'1.0-2' }}</p>
<p>{{ 1234.56 | currency:'CNY':'symbol-narrow' }}</p>

2.4 大小写管道 #

html
<p>{{ 'hello world' | uppercase }}</p>
<p>{{ 'HELLO WORLD' | lowercase }}</p>
<p>{{ 'hello world' | titlecase }}</p>

2.5 JSON管道 #

html
<pre>{{ user | json }}</pre>

2.6 Slice管道 #

html
<p>{{ 'Hello World' | slice:0:5 }}</p>
<p>{{ [1, 2, 3, 4, 5] | slice:1:4 }}</p>

2.7 异步管道 #

typescript
@Component({
  template: `
    <p>{{ data$ | async }}</p>
    <p>{{ user$ | async?.name }}</p>
  `
})
export class ExampleComponent {
  data$ = of('Hello');
  user$ = this.http.get<User>('/api/user');
}

2.8 I18n管道 #

html
<p>{{ date | date:'medium':'':'zh-CN' }}</p>
<p>{{ 1234.56 | currency:'CNY':'symbol':'1.2-2':'zh-CN' }}</p>

三、链式管道 #

html
<p>{{ message | uppercase | slice:0:20 }}</p>
<p>{{ price | currency:'CNY' | uppercase }}</p>
<p>{{ birthday | date:'fullDate' | uppercase }}</p>

四、自定义管道 #

4.1 创建管道 #

bash
ng generate pipe pipes/truncate

4.2 管道实现 #

typescript
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;
  }
}

4.3 使用管道 #

typescript
import { TruncatePipe } from './pipes/truncate.pipe';

@Component({
  selector: 'app-example',
  standalone: true,
  imports: [TruncatePipe],
  template: `
    <p>{{ longText | truncate }}</p>
    <p>{{ longText | truncate:50 }}</p>
    <p>{{ longText | truncate:30:'...' }}</p>
  `
})
export class ExampleComponent {
  longText = '这是一段很长的文本内容,需要进行截断处理';
}

五、实用自定义管道 #

5.1 安全HTML管道 #

typescript
import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';

@Pipe({
  name: 'safeHtml',
  standalone: true
})
export class SafeHtmlPipe implements PipeTransform {
  constructor(private sanitizer: DomSanitizer) {}
  
  transform(value: string): SafeHtml {
    return this.sanitizer.bypassSecurityTrustHtml(value);
  }
}
html
<div [innerHTML]="htmlContent | safeHtml"></div>

5.2 过滤管道 #

typescript
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'filter',
  standalone: true
})
export class FilterPipe implements PipeTransform {
  transform<T>(items: T[], field: keyof T, value: any): T[] {
    if (!items || !field || !value) {
      return items;
    }
    
    return items.filter(item => 
      String(item[field]).toLowerCase().includes(String(value).toLowerCase())
    );
  }
}
html
<ul>
  <li *ngFor="let user of users | filter:'name':searchTerm">
    {{ user.name }}
  </li>
</ul>

5.3 排序管道 #

typescript
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'sortBy',
  standalone: true
})
export class SortByPipe implements PipeTransform {
  transform<T>(items: T[], field: keyof T, order: 'asc' | 'desc' = 'asc'): T[] {
    if (!items || !field) {
      return items;
    }
    
    const sorted = [...items].sort((a, b) => {
      const aVal = a[field];
      const bVal = b[field];
      
      if (aVal < bVal) return -1;
      if (aVal > bVal) return 1;
      return 0;
    });
    
    return order === 'desc' ? sorted.reverse() : sorted;
  }
}
html
<ul>
  <li *ngFor="let user of users | sortBy:'name':'asc'">
    {{ user.name }}
  </li>
</ul>

5.4 文件大小管道 #

typescript
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'fileSize',
  standalone: true
})
export class FileSizePipe implements PipeTransform {
  transform(bytes: number, decimals: number = 2): string {
    if (bytes === 0) return '0 Bytes';
    
    const k = 1024;
    const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    
    return parseFloat((bytes / Math.pow(k, i)).toFixed(decimals)) + ' ' + sizes[i];
  }
}
html
<p>{{ 1024 | fileSize }}</p>
<p>{{ 1048576 | fileSize }}</p>

5.5 时间格式管道 #

typescript
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'timeAgo',
  standalone: true
})
export class TimeAgoPipe implements PipeTransform {
  transform(value: Date | string): string {
    const date = new Date(value);
    const now = new Date();
    const seconds = Math.floor((now.getTime() - date.getTime()) / 1000);
    
    const intervals = {
      年: 31536000,
      月: 2592000,
      周: 604800,
      天: 86400,
      小时: 3600,
      分钟: 60
    };
    
    for (const [unit, secondsInUnit] of Object.entries(intervals)) {
      const interval = Math.floor(seconds / secondsInUnit);
      if (interval >= 1) {
        return `${interval}${unit}前`;
      }
    }
    
    return '刚刚';
  }
}
html
<p>{{ createdAt | timeAgo }}</p>

六、纯管道与非纯管道 #

6.1 纯管道(默认) #

typescript
@Pipe({
  name: 'purePipe',
  pure: true  // 默认值
})
export class PurePipe implements PipeTransform {
  transform(value: any) {
    console.log('纯管道执行');
    return value;
  }
}

特点:

  • 只在输入值变化时执行
  • 性能更好
  • 适用于纯函数

6.2 非纯管道 #

typescript
@Pipe({
  name: 'impurePipe',
  pure: false
})
export class ImpurePipe implements PipeTransform {
  transform(value: any) {
    console.log('非纯管道执行');
    return value;
  }
}

特点:

  • 每次变更检测都执行
  • 性能较差
  • 适用于需要频繁更新的场景

6.3 非纯管道示例 #

typescript
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'filterArray',
  pure: false
})
export class FilterArrayPipe implements PipeTransform {
  transform<T>(items: T[], predicate: (item: T) => boolean): T[] {
    return items.filter(predicate);
  }
}

七、管道最佳实践 #

7.1 使用纯管道 #

typescript
// 推荐:纯管道
@Pipe({
  name: 'truncate',
  pure: true
})

// 谨慎使用:非纯管道
@Pipe({
  name: 'filter',
  pure: false
})

7.2 避免复杂计算 #

typescript
// 不推荐:复杂计算
@Pipe({ name: 'complex' })
export class ComplexPipe implements PipeTransform {
  transform(value: any) {
    // 复杂计算...
    return result;
  }
}

// 推荐:预先计算
this.formattedData = this.formatData(this.rawData);

7.3 缓存结果 #

typescript
@Pipe({
  name: 'expensive',
  pure: true
})
export class ExpensivePipe implements PipeTransform {
  private lastInput: any;
  private lastResult: any;
  
  transform(value: any) {
    if (value === this.lastInput) {
      return this.lastResult;
    }
    
    this.lastInput = value;
    this.lastResult = this.expensiveCalculation(value);
    return this.lastResult;
  }
  
  private expensiveCalculation(value: any) {
    // 复杂计算
  }
}

八、总结 #

管道 说明
date 日期格式化
number 数字格式化
currency 货币格式化
uppercase 大写转换
lowercase 小写转换
titlecase 标题大小写
json JSON格式化
slice 数组/字符串截取
async 异步数据绑定
自定义管道 自定义转换逻辑

下一步:动态组件

最后更新:2026-03-26