介绍

Pipe 类似于 Template Syntax,它的用途是 transform value for display。

参考:

Docs – Understanding Pipes

DatePipe

一个简单的例子。

我有一个 JavaScript 的 Date value,我要 display 给用户看,那你不可能直接 .toString() 就显示嘛,很丑丫。

所以呢,它就需要一个 transformation。而按照职责划分(Thinking in Angular Way),这个任务显然是属于 view 的 logic。

所以应该在 HTML 去表达。于是就有了 Pipe 写法。

<h1>{{ today }}</h1>
<h1>{{ today | date : 'dd MMM yyyy' }}</h1>
<h1>{{ today | date : 'fullDate' }}</h1>

pipe 的语法是这样的 {{  value | pipeName : paramter1 : paramter2  }}

你可以把它看作是一个 function call。

比如现在我们使用的 DatePipe,就是 const displayDate = date(today , 'dd MMM yyyy');

today 是 component property,是一个 new Date()

| pipe 就是启动 pipe transform。

date 是 Angular build-in 的 DatePipe,Angular build-in 了许多 pipe,每一个负责不同的 transform,顾名思义 DatePipe 自然是用于 transform date value。

注:要使用 Angular build-in 的 Pipe,必须在 Component metadata imports CommonModule 哦。

: 分号表示要输入 paramters

'dd MMM yyyy' 则是输入的 parameter,声明想 transform to 什么 date format。(p.s. 想知道所有 Angular 支持的 format, 参考这里

效果

UpperCasePipe, LowerCasePipe, TitleCase

<h1>{{ 'Hello World' | uppercase }}</h1>
<h1>{{ 'Hello World' | lowercase }}</h1>
<h1>{{ 'hello world' | titlecase }}</h1>

效果

DecimalPipe

在 JavaScript, 我们可以用下划线来分割 number 让它好看一点

<h1>{{ 1_000_000 }}</h1>

但当渲染 HTML 的时候,它会被 number.toString(),结果变成

DecimalPipe 可用于解决这种问题。

<h1>{{ 1_000_000 | number }}</h1>

注:虽然它叫 DecimalPipe,但使用时是 | number 哦。

效果

除此之外,number 还支持一个 digit setting,

它可以控制小数点前后号码,最少最多几个数字。

语法是

{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}

for example '2.2-4' 表示小数点前最少要 2 digit,小数点后最少 2 最多 4 个 digit。

不够 digit 时它会用零补上,超过 digit 时它会四舍五入进位.

<h1>{{ 1.10 | number : '2.2-4' }}</h1>   <!--01.10 前面补上了一个 0 -->
<h1>{{ 0.1 | number : '.2-4' }}</h1> <!--0.10 后面补上了一个 0-->
<h1>{{ .123 | number : '1.2-4' }}</h1> <!--0.123 前面补上了一个 0-->
<h1>{{ .12345 | number : '1.2-4' }}</h1> <!--0.1235 后面四舍五入进位-->

注:它的 default 是 '1.0-3'。另外,小数点前只能控制最少 digit,不能控制最多。

PercentPipe

PercentPipe 的作用时把 0.57 变成 '57%'

<h1>{{ 0.57 | percent }}</h1>

效果

它也支持 digit setting

<h1>{{ 0.575 | percent : '.0' }}</h1> <!--58%-->

p.s. '.0' 相等于 '1.0-0',best practice 是写完整的 '1.0-0'。

CurrencyPipe

CurrentPipe 会 transform number 变成带有 current code。

<h1>{{ 100 | currency : 'USD' }}</h1>                   <!-- $100.00 默认是显示 symbol 哦 -->
<h1>{{ 100 | currency : 'USD' : 'code' }}</h1> <!-- USD100.00 -->
<h1>{{ 100 | currency : 'USD' : 'symbol' }}</h1> <!-- $100.00 -->
<h1>{{ 100 | currency : 'MYR' : 'symbol-narrow' }}</h1> <!-- RM100.00 -->
<h1>{{ 100 | currency : 'MYR' : 'M$ ' }}</h1> <!-- M$100.00 -->

第一个参数是 currency code ISO 4217

第二个参数是 display mode,有好几个选择:

  1. 'code' 表示显示 currency code

  2. 'symbol' 表示显示符号就够了。Angular 会把 code 转成对应的 symbol,比如 USD -> $

  3. 'symbol-narrow' 也是显示 symbol 但是是 "narrow" 版,马来西亚的 symbol-narrow 是 'RM' Ringgit Malaysia 的缩写

  4. 'whatever string',如果 code, symbol, symbol-narrow 都不是你要的,那可以提供一个 string 作为 display。

第三个参数是 digit setting,默认是 '1.2-2'。

Global override default currency

default currency 是 USD,我们可以通过 Dependancy Injection 的方式提供一个全局 currency 替换掉 default currency。

import { DEFAULT_CURRENCY_CODE, type ApplicationConfig } from '@angular/core';

export const appConfig: ApplicationConfig = {
providers: [{ provide: DEFAULT_CURRENCY_CODE, useValue: 'MYR' }],
};

JsonPipe, SlicePipe, AsyncPipe

其它 build-in pipes.

<h1>{{ { name: 'Derrick' } | json }}</h1> <!-- { "name": "Derrick" } -->
<h1>{{ ['a', 'b', 'c', 'd'] | slice : 1 : -1 }}</h1> <!-- ['b', 'c'] -->
<h1>{{ value$ | async }}</h1> <!-- value1---value2---value3 -->

async pipe 常用于处理 PromiseRxJS Stream

它内部会 subscribe 和自动 unsubscribe Observable 非常方便。

当 Observable 处于未发布的时候,async pipe 会先返回 null 作为初始值。

Global Configuration

上面例子中有些 Pipe 使用时需要传入 parameters。比如 | currency : 'USD', | date : 'dd MMM yyyy'。

许多时候整个项目的 currency、date format 都是一致的。这时就需要一个 global config。

Pipe 的 global config 是利用 DI 来实现的。

如果想设置全局,可以到 app.config.ts 写入 provider 就可以了。

import { DATE_PIPE_DEFAULT_OPTIONS, DatePipeConfig } from '@angular/common';
import { ApplicationConfig, DEFAULT_CURRENCY_CODE } from '@angular/core'; export const appConfig: ApplicationConfig = {
providers: [
{ provide: DEFAULT_CURRENCY_CODE, useValue: 'USD' },
{
provide: DATE_PIPE_DEFAULT_OPTIONS,
useValue: {
dateFormat: 'dd MMM yyyy',
timezone: '+0800', // if empty string, then Angular will use end-user's local system timezone
} satisfies DatePipeConfig,
},
],
};

DEFAULT_CURRENCY_CODE 和 DATE_PIPE_DEFAULT_OPTIONS 是 InjectionToken

Use Pipe Transform in Any Where

大部分 Angular pipe 都提供了底层方法,所以可以用在任何地方

import { formatDate, formatCurrency, formatNumber, formatPercent} from '@angular/common';

console.log(formatDate(new Date(), 'dd MMM yyyy', 'en-US')); // 06 Apr 2023
console.log(formatCurrency(100, 'en-US', 'RM', 'MYR')); // RM100.00

当然,它们不在 DI 的 cover 范围,也就无法使用 global config 了。

如果想在组件内使用 pipe,可以通过 DI

DatePipe by default 是没有在 DI 中的,所以我们需要 provide。

可以在 app.config.ts provide,也可以通过 Component metadata provide(区别是什么,我会在接下来章节讲解,这里不展开)

然后注入就可以使用了。这样它就能使用到 global config 了。

Custom Pipe

上面提及的都是 Angular build-in 的 pipe。虽然挺多的,但在真实项目中依然会不够用。

幸好,Angular 允许我们自定义 Pipe。(Angular build-in 的 pipe 也是用同一种方式添加进项目的哦)

我们尝试做一个 AgoPipe

CLI command

ng g p ago

p for pipe

一开始长这样

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

@Pipe({
name: 'ago',
standalone: true
})
export class AgoPipe implements PipeTransform {
transform(value: unknown, ...args: unknown[]): unknown {
return null;
}
}

接着我们加入逻辑

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

// 如果有需要 global config
export interface AgoPipeConfig {}
export const AGO_PIPE_CONFIG_TOKEN = new InjectionToken<AgoPipeConfig>(
'AgoPipeConfig'
);
// 底层方法
export function formatAgo(
date: Date,
param1?: string,
config: AgoPipeConfig | null = null
) {
console.log([date, param1, config]); // value, parameter, config
// do any transformation (这里我省略掉具体实现方法)
return '7 days ago';
} @Pipe({
name: 'ago',
standalone: true,
})
export class AgoPipe implements PipeTransform {
private agoPipeConfig = inject(AGO_PIPE_CONFIG_TOKEN, { optional: true }); // 注入 global config transform(date: Date, param1?: string): string {
// 调用底层方法
return formatAgo(date, param1, this.agoPipeConfig);
}
}

看注释理解,里面包含了 global config 和底层方法。类似 Angular 的 formatCurrency 和 DEFAULT_CURRENCY_CODE

在 app.config.ts 提供 global config

import { ApplicationConfig } from '@angular/core';
import { AgoPipeConfig, AGO_PIPE_CONFIG_TOKEN } from './ago.pipe'; export const appConfig: ApplicationConfig = {
providers: [
{ provide: AGO_PIPE_CONFIG_TOKEN, useValue: {} satisfies AgoPipeConfig },
],
};

在组件 imports AgoPipe

@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, AgoPipe], // imports AgoPipe
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent {
constructor() {}
today = new Date();
}

BTW, Angular build-in 的 pipe 是封装在 CommonModule 里的。

最后,在 template 使用

<h1>{{ today | ago }}</h1>

这样就可以了。

Pure

@Pipe({
name: 'ago',
standalone: true,
pure: false
})

pure 是 Pipe 的一个 config。默认是 true。它表示只有当 value 改变时才需要重新运行 transform 方法。

这个规则在某一些情况是不对的,比如 transform 依赖 config 的时候。因为不只是 value 改变会影响 transform 的结果,config 改变也可能影响 transform 的结果。

所以在这个情况下,我们需要把 pure 设置成 false。设置成 false 以后,不仅仅是当 value 改变,只要当前的组件 re-render(DoCheck 执行不代表 re-render 哦,要 dirty 才会 re-render),transform 就会执行。

至于这个 re-render 是什么概念,我会在后面 Change Detection 章节详细讲解。

Override Built-in Pipe

built-in pipe 很好,但也不是万能的,遇到不合适的场景还是需要魔改一下。这里提供一些魔改方案。

让 CurrencyPipe 支持 Big.js

CurrencyPipe 只支持 number | string | null | undefined

假如我的 value 类型是 Big.js 的 Big 对象,那就不能直接使用了。

最直接的解决方法就是调用 Big.toNumber 把 Big 对象转换成 number 类型。

<h1>{{ price.toNumber() | currency : 'MYR' : 'symbol-narrow' : '.2-2' }}</h1>

不过每次都要调用就很烦啊,我们能不能让 current pipe 支持 Big 对象,在调用 toNumber 封装起来?

可以,直接继承 CurrencyPipe 然后 override transform 方法

@Pipe({
name: 'currency',
standalone: true,
})
export class MyCurrencyPipe extends CurrencyPipe implements PipeTransform {
override transform(
value: number | Decimal | string,
currencyCode?: string,
display?: 'code' | 'symbol' | 'symbol-narrow' | string | boolean,
digitsInfo?: string,
locale?: string,
): string | null;
override transform(
value: null | Decimal | undefined,
currencyCode?: string,
display?: 'code' | 'symbol' | 'symbol-narrow' | string | boolean,
digitsInfo?: string,
locale?: string,
): null;
override transform(
value: number | Decimal | string | null | undefined,
currencyCode?: string,
display?: 'code' | 'symbol' | 'symbol-narrow' | string | boolean,
digitsInfo?: string,
locale?: string,
): string | null;
override transform(
value: number | Decimal | string | null | undefined,
currencyCode?: string,
display?: 'code' | 'symbol' | 'symbol-narrow' | string | boolean,
digitsInfo?: string,
locale?: string,
): string | null {
const convertedValue = value instanceof Decimal ? value.toNumber() : value;
return super.transform(convertedValue, currencyCode, display, digitsInfo, locale);
}
}

transform 方法本来就有很多 overload,所以我们得 override 每一个才行,真麻烦,

接着 imports 就可以了。

效果

没有报错了。

注:Pipe name 用回 'currency' 也不要紧,不会撞名字的。Angular compiler 会优先选择我们定义的 Pipe。

目录

上一篇 Angular 18+ 高级教程 – Component 组件 の Attribute Directives 属性型指令

下一篇 Angular 18+ 高级教程 – Change Detection & Ivy rendering engine

想查看目录,请移步 Angular 18+ 高级教程 – 目录

喜欢请点推荐,若发现教程内容以新版脱节请评论通知我。happy coding

Angular 18+ 高级教程 – Component 组件 の Pipe 管道的更多相关文章

  1. python学习笔记——multiprocessing 多进程组件 Pipe管道

    进程间通信(IPC InterProcess Communication)是值在不同进程间传播或交换信息. IPC通过有管道(无名管道 和 有名 / 命名管道).消息队列.共享存储 / 内容.信号量. ...

  2. Angular CLI 使用教程指南参考

    Angular CLI 使用教程指南参考 Angular CLI 现在虽然可以正常使用但仍然处于测试阶段. Angular CLI 依赖 Node 4 和 NPM 3 或更高版本. 安装 要安装Ang ...

  3. vue 基础-->进阶 教程(3):组件嵌套、组件之间的通信、路由机制

    前面的nodejs教程并没有停止更新,因为node项目需要用vue来实现界面部分,所以先插入一个vue教程,以免不会的同学不能很好的完成项目. 本教程,将从零开始,教给大家vue的基础.高级操作.组件 ...

  4. 一篇文章看懂angularjs component组件

     壹 ❀ 引 我在 angularjs 一篇文章看懂自定义指令directive 一文中详细介绍了directive基本用法与完整属性介绍.directive是个很神奇的存在,你可以不设置templa ...

  5. 分享25个新鲜出炉的 Photoshop 高级教程

    网络上众多优秀的 Photoshop 实例教程是提高 Photoshop 技能的最佳学习途径.今天,我向大家分享25个新鲜出炉的 Photoshop 高级教程,提高你的设计技巧,制作时尚的图片效果.这 ...

  6. Python第十一天 异常处理 glob模块和shlex模块 打开外部程序和subprocess模块 subprocess类 Pipe管道 operator模块 sorted函数 os模块 hashlib模块 platform模块 csv模块

    Python第十一天    异常处理  glob模块和shlex模块    打开外部程序和subprocess模块  subprocess类  Pipe管道  operator模块   sorted函 ...

  7. [转帖]tar高级教程:增量备份、定时备份、网络备份

    tar高级教程:增量备份.定时备份.网络备份 作者: lesca 分类: Tutorials, Ubuntu 发布时间: 2012-03-01 11:42 ė浏览 27,065 次 61条评论 一.概 ...

  8. Siki_Unity_2-9_C#高级教程(未完)

    Unity 2-9 C#高级教程 任务1:字符串和正则表达式任务1-1&1-2:字符串类string System.String类(string为别名) 注:string创建的字符串是不可变的 ...

  9. angular里使用vue/vue组件怎么在angular里用

    欢迎加入前端交流群交流知识&&获取视频资料:749539640 如何在angularjs(1)中使用vue参考: https://medium.com/@graphicbeacon/h ...

  10. angularjs中directive指令与component组件有什么区别?

     壹 ❀ 引 我在前面花了两篇博客分别系统化介绍了angularjs中的directive指令与component组件,当然directive也能实现组件这点毋庸置疑.在了解完两者后,即便我们知道co ...

随机推荐

  1. JDBC第二天:防sql攻击

    1 什么是SQL攻击 在需要用户输入的地方,用户输入的是SQL语句的片段,最终用户输入的SQL片段与我们DAO中写的SQL语句合成一个完整的SQL语句!例如用户在登录时输入的用户名和密码都是为SQL语 ...

  2. Django model 层之事务管理总结

    Django model 层之事务管理总结 by:授客 QQ:1033553122 实践环境 Python版本:python-3.4.0.amd64 下载地址:https://www.python.o ...

  3. 2024NOI联合省选游记

    人生当中成功只是一时的,而失败却是主旋律. 不太好的的阅读体验 本文作者:xxxalq 所谓游记,顾名思义就是指游玩所记,所以重点在玩而不在省选. 由于没有参加 \(\text{NOIP}\),导致我 ...

  4. android常用布局基础学习

    总结:可水平放置可垂直放置也可穿插使用,默认为水平 <!--我在第一次使用权重的时候忽视了本线性布局中的宽度与高度,如果要使用权重,请将线性布局的最初大小设置为match_parent,否则不会 ...

  5. 写写Redis十大类型bitmap的常用命令

    其实这些命令官方上都有,而且可读性很强,还有汉化组翻译的http://redis.cn/commands.html,不过光是练习还是容易忘,写一写博客记录一下 bitmap 位图,是由0和1状态表现的 ...

  6. docker redis集群实验

    集群redis 分片+高可用+负载均衡 master + slave{1..5} 一个挂了另一个顶上 通过脚本创建6个redis配置文件 [root@docker ~]# for port in $( ...

  7. python面向对象游戏练习:好人坏人手枪手榴弹

    python面向对象游戏练习:好人坏人手枪手榴弹 主要是多态的练习,对象作为参数传给方法使用 1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 ...

  8. mujoco安装报错:mujoco_py/cymj.pyx:67:5: Exception check on 'c_warning_callback' will always require the GIL to be acquired.

    参考: https://blog.csdn.net/weixin_49373427/article/details/131981583 https://blog.csdn.net/CCCDeric/a ...

  9. ubuntu22.04 终端显示数字剑雨

    数字剑雨是读大学时候常用的屏保,这些年基本也再没有用过,不经意间想到了这个曾经的屏保,发现Ubuntu原版的桌面系统是没有屏保的,又不想换桌面系统,想想还是单独安装一下这个数字剑雨吧. 在Ubuntu ...

  10. Spring Boot 基于 SCRAM 认证集成 Kafka 的详解

    一.说明 在现代微服务架构中,Kafka 作为消息中间件被广泛使用,而安全性则是其中的一个关键因素.在本篇文章中,我们将探讨如何在 Spring Boot 应用中集成 Kafka 并使用 SCRAM ...