MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

利用Angular管道实现日期格式转换

2021-10-082.3k 阅读

Angular管道基础

什么是管道

在Angular中,管道是一种用于对数据进行转换和格式化的机制。它们提供了一种简单而有效的方式,将数据从一种形式转换为另一种形式,以满足特定的显示需求。例如,你可以使用管道来格式化日期、货币、数字,或者对文本进行大小写转换等。管道本质上是一个类,它实现了 PipeTransform 接口,该接口要求实现 transform 方法,这个方法接收输入值并返回转换后的值。

管道的使用方式

管道在模板中使用,通过 | 符号进行调用。例如,要将一个字符串转换为大写,可以这样使用内置的 UpperCasePipe

<p>{{ 'hello world' | uppercase }}</p>

在上述代码中,'hello world' 是输入值,uppercase 是管道名,管道将输入字符串转换为大写并显示在 <p> 标签中。

日期格式转换需求

在前端开发中,日期的显示格式往往需要根据不同的业务场景和用户需求进行调整。常见的日期格式有 YYYY-MM-DDMM/DD/YYYYDD - MMM - YYYY 等。例如,在一个财务应用中,可能需要以 YYYY - MM - DD 的格式显示交易日期,而在一个日历应用中,可能更倾向于 DD - MMM - YYYY 的格式,以便用户更直观地识别日期。

Angular内置日期管道

DatePipe介绍

Angular提供了一个内置的 DatePipe,用于日期格式转换。DatePipe 可以根据不同的格式字符串,将 Date 对象或表示日期的字符串转换为指定格式的日期字符串。它的使用非常方便,并且支持多种预定义的格式。

DatePipe的使用

  1. 基本使用 假设在组件的类中有一个日期属性:
import { Component } from '@angular/core';

@Component({
  selector: 'app-date-example',
  templateUrl: './date - example.component.html'
})
export class DateExampleComponent {
  currentDate = new Date();
}

在模板文件 date - example.component.html 中,可以这样使用 DatePipe

<p>{{ currentDate | date }}</p>

上述代码会以默认格式显示当前日期,默认格式会根据用户浏览器的语言和区域设置进行调整。

  1. 指定格式字符串 如果要显示特定格式的日期,可以传入格式字符串。例如,要以 YYYY - MM - DD 的格式显示日期:
<p>{{ currentDate | date: 'yyyy - MM - dd' }}</p>

这里的 yyyy 表示4位年份,MM 表示2位月份,dd 表示2位日期。不同的格式字符组合可以实现各种日期格式需求。例如,要显示 MM/DD/YYYY 格式:

<p>{{ currentDate | date: 'MM/dd/yyyy' }}</p>
  1. 使用预定义格式 DatePipe 还支持一些预定义的格式,如 'short''medium''long''full'。这些预定义格式会根据用户的区域设置来调整日期的显示。
  • short 格式:
<p>{{ currentDate | date:'short' }}</p>

在英语(美国)区域设置下,可能显示为 1/1/20 这种形式。

  • medium 格式:
<p>{{ currentDate | date:'medium' }}</p>

在英语(美国)区域设置下,可能显示为 Jan 1, 2020

  • long 格式:
<p>{{ currentDate | date: 'long' }}</p>

在英语(美国)区域设置下,可能显示为 January 1, 2020

  • full 格式:
<p>{{ currentDate | date: 'full' }}</p>

在英语(美国)区域设置下,可能显示为 Wednesday, January 1, 2020

  1. 处理不同时区 DatePipe 还支持处理不同时区的日期。可以通过传入时区偏移量或时区标识符来指定日期的时区。例如,要将日期转换为协调世界时(UTC)并显示:
<p>{{ currentDate | date: 'yyyy - MM - dd HH:mm:ss zzz': 'UTC' }}</p>

这里的 zzz 表示时区名称,'UTC' 是时区标识符。如果要使用时区偏移量,例如将日期转换为比 UTC 早2小时的时区,可以这样:

<p>{{ currentDate | date: 'yyyy - MM - dd HH:mm:ss zzz': '+0200' }}</p>

创建自定义日期管道

为什么需要自定义日期管道

尽管Angular的内置 DatePipe 功能强大,但在某些情况下,可能需要更特定的日期格式转换逻辑。例如,业务需求要求以 YYYY年MM月DD日 星期X 的格式显示日期,这种格式在内置管道中无法直接实现,此时就需要创建自定义日期管道。

创建步骤

  1. 生成管道 使用Angular CLI可以快速生成一个管道。在项目目录下执行以下命令:
ng generate pipe custom - date

这会在 src/app 目录下生成 custom - date.pipe.ts 文件,内容如下:

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

@Pipe({
  name: 'customDate'
})
export class CustomDatePipe implements PipeTransform {
  transform(value: unknown, ...args: unknown[]): unknown {
    return null;
  }
}
  1. 实现转换逻辑 修改 transform 方法来实现日期格式转换。假设要实现 YYYY年MM月DD日 星期X 的格式,代码如下:
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'customDate'
})
export class CustomDatePipe implements PipeTransform {
  transform(value: string | Date, ...args: unknown[]): string {
    const date = typeof value ==='string'? new Date(value) : value;
    if (isNaN(date.getTime())) {
      return '';
    }
    const year = date.getFullYear();
    const month = this.padZero(date.getMonth() + 1);
    const day = this.padZero(date.getDate());
    const weekday = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'][date.getDay()];
    return `${year}年${month}月${day}日 ${weekday}`;
  }

  private padZero(num: number): string {
    return num.toString().padStart(2, '0');
  }
}

在上述代码中,首先判断输入值是否为字符串,如果是则转换为 Date 对象。然后获取年份、月份、日期和星期几,并使用 padZero 方法对月份和日期进行补零操作,最后按照指定格式返回日期字符串。

  1. 在模板中使用自定义管道 在组件的模板中使用自定义管道,假设组件中有一个日期属性:
import { Component } from '@angular/core';

@Component({
  selector: 'app - custom - date - example',
  templateUrl: './custom - date - example.component.html'
})
export class CustomDateExampleComponent {
  customDate = new Date();
}

在模板文件 custom - date - example.component.html 中:

<p>{{ customDate | customDate }}</p>

这样就可以按照自定义的格式显示日期了。

在服务中使用日期管道

服务中使用管道的场景

在一些情况下,可能需要在服务中对日期进行格式化,而不是仅仅在模板中。例如,在一个数据处理服务中,需要将从后端获取的日期数据进行格式化后再返回给组件。

具体实现

  1. 注入DatePipe到服务 首先创建一个服务,例如 DataService
ng generate service data

data.service.ts 中注入 DatePipe

import { Injectable } from '@angular/core';
import { DatePipe } from '@angular/common';

@Injectable({
  providedIn: 'root'
})
export class DataService {
  constructor(private datePipe: DatePipe) {}

  formatDate(date: string | Date, format: string): string {
    return this.datePipe.transform(date, format) || '';
  }
}

在上述代码中,通过构造函数注入了 DatePipe,并定义了一个 formatDate 方法,该方法接收日期和格式字符串,使用 DatePipe 进行日期格式化并返回结果。

  1. 在组件中使用服务 在组件中注入 DataService 并使用其方法:
import { Component } from '@angular/core';
import { DataService } from './data.service';

@Component({
  selector: 'app - service - date - example',
  templateUrl: './service - date - example.component.html'
})
export class ServiceDateExampleComponent {
  serviceDate = new Date();
  formattedDate: string;

  constructor(private dataService: DataService) {
    this.formattedDate = this.dataService.formatDate(this.serviceDate, 'yyyy - MM - dd');
  }
}

在模板文件 service - date - example.component.html 中:

<p>{{ formattedDate }}</p>

这样就实现了在服务中使用日期管道对日期进行格式化,并在组件中显示格式化后的日期。

处理日期格式转换中的异常情况

输入值异常

在日期格式转换过程中,输入值可能不是有效的日期格式。例如,用户可能输入了一个错误的日期字符串,或者后端返回的数据格式不正确。在使用 DatePipe 或自定义日期管道时,需要对这种情况进行处理。

  1. DatePipe的处理 当使用 DatePipe 时,如果输入值不是有效的日期,它会返回一个空字符串(在某些情况下也可能返回原始输入值,具体取决于Angular版本和设置)。例如:
<p>{{ 'not a date' | date: 'yyyy - MM - dd' }}</p>

上述代码会显示为空字符串。

  1. 自定义管道的处理 在自定义管道中,需要在 transform 方法中显式处理无效日期。例如,在前面的 CustomDatePipe 中,已经通过 isNaN(date.getTime()) 判断输入值是否为有效的日期,如果不是则返回空字符串。

时区相关异常

  1. 无效的时区标识符 当在 DatePipe 中使用时区标识符时,如果提供的标识符无效,可能会导致异常。例如,使用了一个不存在的时区标识符:
<p>{{ currentDate | date: 'yyyy - MM - dd HH:mm:ss zzz': 'invalid - zone' }}</p>

这种情况下,Angular可能会抛出一个错误。为了避免这种情况,应该确保使用的时区标识符是有效的。可以参考IANA时区数据库中的有效标识符列表。

  1. 时区偏移量格式错误 时区偏移量的格式也需要正确。例如,偏移量应该是 +HHMM-HHMM 的格式。如果格式错误,如 +HMM,也可能导致异常。在处理时区偏移量时,需要对输入进行验证,确保格式正确。

性能优化

管道的纯与非纯模式

  1. 纯管道 Angular管道分为纯管道和非纯管道。默认情况下,管道是纯管道。纯管道只有在输入值发生纯变化时才会执行转换。所谓纯变化,对于基本类型(如字符串、数字等)是指值的改变,对于对象和数组是指引用的改变。例如,在使用 DatePipe 时,如果 Date 对象的引用没有改变,即使日期的实际值发生了变化(通过修改日期的属性),DatePipe 也不会重新执行转换。这是因为纯管道通过引用比较来判断是否需要重新计算。

  2. 非纯管道 非纯管道在每次变化检测运行时都会执行转换,无论输入值是否发生了实际变化。非纯管道可以通过在 @Pipe 装饰器中设置 pure: false 来定义。在处理日期时,通常不需要将日期管道设置为非纯管道,因为日期对象的变化通常伴随着引用的变化(例如重新创建 Date 对象),纯管道可以满足大多数需求。但在某些特殊情况下,如在一个频繁更新的应用中,日期的变化是通过修改现有 Date 对象的属性实现的,此时可能需要将日期管道设置为非纯管道。不过,使用非纯管道会增加性能开销,因为每次变化检测都会执行管道的 transform 方法,所以应该谨慎使用。

缓存转换结果

在一些情况下,如果日期格式转换的频率较高,并且输入值相对稳定,可以考虑缓存转换结果。例如,在一个显示多个日期的表格中,每个日期的格式可能相同,并且这些日期在页面加载后很少发生变化。可以在服务中添加一个缓存机制,避免重复计算。

  1. 简单缓存实现DataService 中添加缓存功能:
import { Injectable } from '@angular/core';
import { DatePipe } from '@angular/common';

@Injectable({
  providedIn: 'root'
})
export class DataService {
  private dateCache: { [key: string]: string } = {};

  constructor(private datePipe: DatePipe) {}

  formatDate(date: string | Date, format: string): string {
    const cacheKey = `${date}-${format}`;
    if (this.dateCache[cacheKey]) {
      return this.dateCache[cacheKey];
    }
    const result = this.datePipe.transform(date, format) || '';
    this.dateCache[cacheKey] = result;
    return result;
  }
}

在上述代码中,使用一个对象 dateCache 作为缓存,缓存键由日期和格式字符串组成。每次调用 formatDate 方法时,先检查缓存中是否有对应的结果,如果有则直接返回,否则进行日期格式化并将结果存入缓存。

与国际化(i18n)的结合

国际化与日期格式

在全球化的应用中,日期格式需要根据不同的语言和区域设置进行调整。Angular的国际化(i18n)功能可以很好地与日期管道结合,实现日期格式的本地化。

配置i18n

  1. 设置区域设置 首先需要在应用的 main.ts 文件中导入并设置区域信息。例如,要设置为英语(美国)区域:
import { platformBrowserDynamic } from '@angular/platform - browser - dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app/app.module';
import { LOCALE_ID } from '@angular/core';
import localeEn from '@angular/common/locales/en';
import { registerLocaleData } from '@angular/common';

registerLocaleData(localeEn);

enableProdMode();
platformBrowserDynamic().bootstrapModule(AppModule, {
  providers: [{ provide: LOCALE_ID, useValue: 'en - US' }]
})
 .catch(err => console.error(err));
  1. 使用i18n与DatePipe 在模板中使用 DatePipe 时,它会根据设置的区域信息自动调整日期格式。例如,对于 short 格式:
<p>{{ currentDate | date:'short' }}</p>

在英语(美国)区域设置下,可能显示为 1/1/20,而在法语(法国)区域设置下,可能显示为 01/01/20

  1. 自定义管道与i18n 如果使用自定义日期管道,也可以结合i18n实现本地化。例如,对于前面的 CustomDatePipe,可以根据区域设置调整星期几的显示。可以通过注入 LOCALE_ID 并根据不同的区域设置返回不同的星期几名称:
import { Pipe, PipeTransform } from '@angular/core';
import { LOCALE_ID } from '@angular/core';
import { Inject } from '@angular/core';

@Pipe({
  name: 'customDate'
})
export class CustomDatePipe implements PipeTransform {
  constructor(@Inject(LOCALE_ID) private locale: string) {}

  transform(value: string | Date, ...args: unknown[]): string {
    const date = typeof value ==='string'? new Date(value) : value;
    if (isNaN(date.getTime())) {
      return '';
    }
    const year = date.getFullYear();
    const month = this.padZero(date.getMonth() + 1);
    const day = this.padZero(date.getDate());
    let weekday;
    if (this.locale.startsWith('en')) {
      weekday = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][date.getDay()];
    } else if (this.locale.startsWith('fr')) {
      weekday = ['dimanche', 'lundi','mardi','mercredi', 'jeudi','vendredi','samedi'][date.getDay()];
    } else {
      weekday = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'][date.getDay()];
    }
    return `${year}年${month}月${day}日 ${weekday}`;
  }

  private padZero(num: number): string {
    return num.toString().padStart(2, '0');
  }
}

这样,自定义日期管道也能根据不同的区域设置显示本地化的日期格式。

通过以上对Angular中利用管道实现日期格式转换的详细介绍,包括内置管道的使用、自定义管道的创建、在服务中的应用、异常处理、性能优化以及与国际化的结合,开发者可以全面掌握日期格式转换的相关技术,为开发高质量的前端应用提供有力支持。无论是简单的日期显示需求,还是复杂的国际化日期格式处理,都能够通过这些方法有效地实现。