Introducción a Angular y Fundamentos

1

Desarrollo de Aplicaciones Modernas con Angular

2

Creando tu primer proyecto en Angular

3

Creación de componentes y rutas en Angular para gestión de tareas

4

String Interpolation en Angular: Transmisión de Datos a Templates

5

Modificación de Propiedades HTML con Angular: Property Binding

6

Eventos en Angular: click, doble click y cambio de input

7

Eventos de Teclado en JavaScript: Uso de KeyDown y Change

8

Reactividad Granular con Signals en Angular

9

Reactividad en HTML: Implementación de Signals en JavaScript

Estructuras de control en Angular

10

Estructuras de Control y Renderizado Dinámico en Angular

11

Gestión Dinámica de Tareas en JavaScript: Creación y Eliminación

12

Estructuración de Tareas en JavaScript: Uso de Interfaces y Typing

13

Actualización de tareas con Angular y programación inmutable

14

Uso de ngIf y ngFor para control de flujos en Angular

15

Pluralización y ngSwitch en Angular para manejo de condiciones múltiples

16

Manejo Avanzado de Formularios Reactivos en Angular

17

Validaciones de Inputs en Formularios Reactivos con Angular

Alistando tu aplicación para producción

18

Validación de formularios y estilos dinámicos en Angular

19

Activación dinámica de clases en Angular con validaciones

20

Edición de Tareas Dinámicas en Aplicaciones Web

21

Estados Computados en Programación Reactiva con Signals

22

Persistencia de Tareas con LocalStorage en Angular

23

Compilación y Optimización de Aplicaciones Angular para Producción

24

Lanzamiento de Aplicaciones con Fiverr Hosting y Angular

25

Nuevas Sintaxis Declarativas en Angular Inspiradas en Svelte

26

Sintaxis y rendimiento de for y switch en JavaScript moderno

27

Migración Automática a Nueva Sintaxis de Angular

Componentes Reutilizables y Comunicación

28

Creación de Tienda en Línea con Angular y Tailwind CSS

29

Creación y Organización de Componentes en Angular

30

Componentes Reutilizables en Angular: Creación y Maquetado de Productos

31

Uso de Angular DevTools para Debugging en Angular

32

Inputs en Angular: Comunicación de Datos entre Componentes

33

Outputs en Angular: Comunicación del Hijo al Padre

34

Maquetación de Galería de Productos con Tailwind CSS

Ciclo de vida de los componentes

35

Ciclo de Vida de Componentes en Angular

36

Creación y Uso de Componentes en Angular: Ciclo de Vida y Comunicación

37

Ciclo de Vida de Componentes en Angular: Eventos Clave

38

Detección de Cambios en Inputs con ngOnChange en Angular

39

Prevención de fugas de memoria con ngOnDestroy en Angular

40

Creación de un Reproductor de Audio con WaveSurfer.js en Angular

41

Mejoras de la página About en e-commerce: audio y contador dinámico

Mejorando la interfaz del producto

42

Componentización y Manejo de Datos en Angular

43

Creación de Headers en Páginas Web con Angular

44

Implementación de Interfaz Gráfica para Carrito de Compras Interactivo

45

Implementación de Reactividad en Carrito de Compras con Angular

46

Gestión de Carrito de Compras en Angular con ngOnChanges

47

Gestión de Estado en Angular: Solución al InputDrilling

48

Gestión de Estado Global en Angular con Servicios y Señales

49

Inyección de Dependencias en Angular: Conceptos y Prácticas

Integración y Datos

50

Conexión de Angular a REST API usando Fake Store API de Platzi

51

Creación de Alias para Imports Cortos en Proyectos JavaScript

52

Transformación de Datos con Pipes en Angular

53

Creación y uso de pipes personalizados en Angular

54

Manipulación de Fechas en Angular con Date Functions

55

Manipulación del DOM con Directivas en Angular

56

Deployment de Aplicaciones Angular en Vercel

Enrutamiento y Navegación

57

Creación de Página 404 en Angular: Manejo de Rutas No Encontradas

58

Implementación de Router Link en Angular para SPA eficiente

59

Implementación de Layouts Compartidos en Angular para Vistas Anidadas

60

Uso de RouterLinkActive en Angular para Navegación Activa

61

Routing en Angular: Creación de Páginas de Detalle de Producto

62

Consulta dinámica de detalles de producto con Angular y REST API

63

Galería de Productos Dinámica con Angular y TypeScript

64

Mejoras en Detalle de Producto: Precio, Carrito e Imagen Activa

Perfeccionando tu e-commerce

65

Filtrado de Productos por Categoría en E-commerce

66

Filtros de Productos con Query Params en Angular

67

Optimización de Aplicaciones con Lazy Loading y Code Splitting

68

Optimización de JavaScript en Angular con Lazy Loading

69

Optimización de Carga de Chunks con Preloading en Angular

70

Migración de Angular a Nueva Sintaxis con ng generate

71

Despliegue de Aplicaciones con Verzal en Entornos Productivos

No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Creación y uso de pipes personalizados en Angular

53/71
Recursos

¿Cómo crear tus propios pipes en Angular?

Crear tus propios pipes en Angular es una habilidad poderosa que te permite manipular y transformar datos de manera eficiente dentro de tus aplicaciones. Aunque Angular ya ofrece una variedad de pipes integrados, crear pipes personalizados te da la flexibilidad de adaptarte a necesidades específicas de formato o transformación de datos. Vamos a sumergirnos en el proceso paso a paso de creación de un pipe en Angular.

¿Cuál es la estructura para crear un pipe en Angular?

Para comenzar a crear un pipe, es necesario tener clara la estructura que Angular requiere. En Angular, los pipes funcionan como transformadores de datos: reciben un valor de entrada, lo transforman y devuelven un valor de salida.

  1. Decoradores en Angular:

    • Un pipe en Angular utiliza el decorador @Pipe, que define su metadato principal, incluido su nombre.
  2. Método transform:

    • El método clave dentro de un pipe es transform. Este método se encarga de realizar la transformación de los datos.

Aquí un ejemplo básico del código de un pipe que voltea una cadena de texto:

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

@Pipe({
  name: 'reverse'
})
export class ReversePipe implements PipeTransform {
  transform(value: string): string {
    return value.split('').reverse().join('');
  }
}

¿Cómo integrar pipes creados con comandos de Angular?

Angular CLI (Command Line Interface) ofrece comandos poderosos y simplificados para generar y gestionar distintos elementos de Angular. Crear un pipe puede ser tan simple como usar el comando adecuado:

  • Comando para crear un pipe:

    ng generate pipe pipe-name
    

Al ejecutar este comando, Angular generará automáticamente los archivos y la estructura necesaria para tu nuevo pipe.

¿Cómo utilizar y conectar pipes?

Una de las maravillas de los pipes en Angular es que no solo puedes crearlos, sino también conectarlos entre sí para realizar transformaciones complejas:

  1. Conectar pipes:

    • Se puede aplicar un pipe tras otro aprovechando la “tubería” de pipes de Angular. Por ejemplo, conectando el pipe uppercase de Angular y el pipe reverse que creamos, podemos transformar un título a mayúsculas y luego invertir el orden de sus caracteres.
  2. Importación de pipes personalizados:

    • Los pipes integrados de Angular están disponibles a través del módulo CommonModule, pero los pipes personalizados requieren ser importados. Debes incluir tu pipe en el módulo adecuado para que tu aplicación sepa de su existencia.

¿Qué otros usos potenciales tienen los pipes personalizados?

Los pipes no solo facilitan la vida del desarrollador al simplificar el código y la vista, sino que también promueven la reutilización y la limpieza del código.

Caso práctico: Pipe “time ago”

  • Un ejemplo de un pipe útil y común podría ser un pipe que convierte fechas en un formato relativo, mostrando cuánto tiempo ha pasado desde una fecha específica. Este formato lo ves frecuentemente en redes sociales y aplicaciones de noticias.

Desafío:

  • Intenta crear un pipe como "time ago" que tome una fecha de entrada y calcule si el evento fue hace unos días, semanas o meses, proporcionando una salida relativa como "hace una semana" o "hace un mes".

Recomendaciones finales

Crear pipes personalizados en Angular es una técnica esencial para cualquier desarrollador que busque maximizar la eficiencia de sus aplicaciones. Mediante el uso de pipes, puedes transformar y visualizar datos de una manera que se ajuste perfectamente a los requisitos de tu aplicación. Anímate a experimentar y construye pipes que sean reutilizables y que hagan tu aplicación más robusta. ¡Sigue aprendiendo y expandiendo tus habilidades en Angular!

Aportes 8

Preguntas 0

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

## Reto ```typescript transform(value: string): string { const date = new Date(value).getTime(); // Not a valid date if (isNaN(date)) { return ''; } const now = Date.now(); const diffInTime = now - date; const diffInHours = Math.round(diffInTime / (1000 \* 3600)); if (diffInHours < 24) { return this.applyFormat(diffInHours, 'hour'); } const diffInDays = Math.floor(diffInHours / 24); if (diffInDays < 30) { return this.applyFormat(diffInDays, 'day'); } const diffInMonths = Math.floor(diffInDays / 30); if (diffInMonths < 12) { return this.applyFormat(diffInDays, 'month'); } const diffInYears = Math.floor(diffInMonths / 12); return this.applyFormat(diffInYears, 'year'); } } ```
Hice esta función un poco larga pero funciona ```typescritp timeAgo(value: Date): String { const now = new Date(); const seconds = Math.round(Math.abs((now.getTime() - value.getTime()) / 1000)); const minutes = Math.round(Math.abs(seconds / 60)); const hours = Math.round(Math.abs(minutes / 60)); const days = Math.round(Math.abs(hours / 24)); const months = Math.round(Math.abs(days / 30.416)); const years = Math.round(Math.abs(days / 365)); if (Number.isNaN(seconds)) { return 'Today'; } else if (seconds <= 45) { return 'a few seconds ago'; } else if (seconds <= 90) { return 'a minute ago'; } else if (minutes <= 45) { return minutes + ' minutes ago'; } else if (minutes <= 90) { return 'an hour ago'; } else if (hours <= 22) { return hours + ' hours ago'; } else if (hours <= 36) { return 'a day ago'; } else if (days <= 25) { return days + ' days ago'; } else if (days <= 45) { return 'a month ago'; } else if (days <= 345) { return months + ' months ago'; } else if (days <= 545) { return 'a year ago'; } else { // (days > 545) return years + ' years ago'; } } } ```
Solucion del reto: ```js import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'timeAgo', standalone: true, }) export class TimeAgoPipe implements PipeTransform { transform(value: string): string { const today = new Date(); const date = new Date(value); const diffYears = today.getFullYear() - date.getFullYear(); const diffMonths = today.getMonth() - date.getMonth(); const diffDays = today.getDay() - date.getDay(); const diffHours = today.getHours() - date.getHours(); const diffMinutes = today.getMinutes() - date.getMinutes(); if (diffYears > 0) { return `${diffYears} month${diffYears > 1 ? 's' : ''} ago`; } if (diffMonths > 0) { return `${diffMonths} year${diffMonths > 1 ? 's' : ''} ago`; } if (diffDays > 0) { return `${diffDays} day${diffDays > 1 ? 's' : ''} ago`; } if (diffHours > 0) { return `${diffHours} hour${diffHours > 1 ? 's' : ''} ago`; } if (diffMinutes > 0) { return `${diffMinutes} minute${diffMinutes > 1 ? 's' : ''} ago`; } return 'just now'; } } ```
Mi solucion: ```js export class TimeAgoPipe implements PipeTransform { transform(dateValue: Date | string | number): string { const entryDate = new Date(dateValue); if (isNaN(entryDate.getTime())) { throw new Error('Fecha no válida'); } const today = new Date(); const diffInYears = today.getFullYear() - entryDate.getFullYear(); const diffInMs = (today.getMonth()+1) - (entryDate.getMonth()+1); const diffInDays = today.getDate() - entryDate.getDate(); const diffInTime = today.getTime() - entryDate.getTime(); const diffInHours = diffInTime / (1000 * 60 * 60); const hours = Math.floor(diffInHours); //const remainingMinutes = (diffInHours - hours) * 60; //const minutes = Math.floor(remainingMinutes); //const seconds = Math.floor((remainingMinutes - minutes) * 60); if(diffInYears == 0 && diffInMs == 0 && diffInDays == 0 && diffInTime > 0){ return hours >= 2 ? `${hours} Hours ago.` : hours === 1 ? `${hours} Hour ago.` : 'Just now.'; } else if(diffInYears == 0 && diffInMs == 0 && diffInDays >= 1){ return diffInDays + (diffInDays >=2 ? ` Days `: ` Day `)+ 'ago.'; } else if(diffInYears == 0 && diffInMs >= 1){ return diffInMs + (diffInMs >=2 ? ` Months `: ` Month `)+ 'ago.'; } else if(diffInYears >= 1){ return diffInYears + (diffInYears >=2 ? ` Years `: ` Year `) + 'ago.'; } else{ return 'Just now.'; } } } ``` Adicionalmente cree un archivo de testing para comprobarlo time-ago.pipe.ts: ```js import { TimeAgoPipe } from './time-ago.pipe'; describe('TimeAgoPipe', () => { let pipe: TimeAgoPipe; beforeEach(() => { pipe = new TimeAgoPipe(); }); it('should return "Just now" for the current time', () => { const result = pipe.transform(new Date()); expect(result).toBe('Just now.'); }); it('should return "1 Hour ago." for 1 hour ago', () => { const oneHourAgo = new Date(); oneHourAgo.setHours(oneHourAgo.getHours() - 1); const result = pipe.transform(oneHourAgo); expect(result).toBe('1 Hour ago.'); }); it('should return "2 Hours ago." for 2 hours ago', () => { const twoHoursAgo = new Date(); twoHoursAgo.setHours(twoHoursAgo.getHours() - 2); const result = pipe.transform(twoHoursAgo); expect(result).toBe('2 Hours ago.'); }); it('should return "1 Day ago." for 1 day ago', () => { const oneDayAgo = new Date(); oneDayAgo.setDate(oneDayAgo.getDate() - 1); const result = pipe.transform(oneDayAgo); expect(result).toBe('1 Day ago.'); }); it('should return "2 Days ago." for 2 days ago', () => { const twoDaysAgo = new Date(); twoDaysAgo.setDate(twoDaysAgo.getDate() - 2); const result = pipe.transform(twoDaysAgo); expect(result).toBe('2 Days ago.'); }); it('should return "1 Month ago." for 1 month ago', () => { const oneMonthAgo = new Date(); oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1); const result = pipe.transform(oneMonthAgo); expect(result).toBe('1 Month ago.'); }); it('should return "2 Months ago." for 2 months ago', () => { const twoMonthsAgo = new Date(); twoMonthsAgo.setMonth(twoMonthsAgo.getMonth() - 2); const result = pipe.transform(twoMonthsAgo); expect(result).toBe('2 Months ago.'); }); it('should return "1 Year ago." for 1 year ago', () => { const oneYearAgo = new Date(); oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1); const result = pipe.transform(oneYearAgo); expect(result).toBe('1 Year ago.'); }); it('should return "2 Years ago." for 2 years ago', () => { const twoYearsAgo = new Date(); twoYearsAgo.setFullYear(twoYearsAgo.getFullYear() - 2); const result = pipe.transform(twoYearsAgo); expect(result).toBe('2 Years ago.'); }); it('should throw an error for invalid dates', () => { expect(() => pipe.transform('invalid-date')).toThrowError('Fecha no válida'); }); }); ```jeje aqui si utilice la ayuda de nuestra IA ayuda, ya que de testing no conozco.
Espero que a alguien le sirva ``` import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'timeAgo', standalone: true})export class TimeAgoPipe implements PipeTransform { transform(value: Date | string | number): string { const now = new Date(); const inputDate = new Date(value); const diff = Math.abs(now.getTime() - inputDate.getTime()); const seconds = Math.floor(diff / 1000); const minutes = Math.floor(seconds / 60); const hours = Math.floor(minutes / 60); const days = Math.floor(hours / 24); const months = Math.floor(days / 30); const years = Math.floor(days / 365); if (years > 0) { return years === 1 ? '1 year ago' : `${years} years ago`; } if (months > 0) { return months === 1 ? '1 month ago' : `${months} months ago`; } if (days > 0) { return days === 1 ? '1 day ago' : `${days} days ago`; } if (hours > 0) { return hours === 1 ? '1 hour ago' : `${hours} hours ago`; } if (minutes > 0) { return minutes === 1 ? '1 minute ago' : `${minutes} minutes ago`; } return seconds === 1 ? '1 second ago' : `${seconds} seconds ago`; } } ```
**Solución al reto:** Inicialmente, como en el modelo `product` la propiedad `creationAt` está tipada como `string | undefined`, debemos eliminar el tipo `undefined` o utilizar el operador de coalescencia nula (`??`) en el binding, de manera que el código quede de la siguiente forma: ```ts {{product.creationAt ?? '' | timeAgo }} ```transform(value: string): string {    const currentDate = new Date();  // Fecha actual    const receiptDate = new Date(value)    const elapsedMilliseconds = currentDate.getTime() - receiptDate.getTime();     const elapsedDays = Math.floor(elapsedMilliseconds / (1000 \* 60 \* 60 \* 24));    const elapsedHours = Math.floor((elapsedMilliseconds % (1000 \* 60 \* 60 \* 24)) / (1000 \* 60 \* 60));    const elapsedMinutes = Math.floor((elapsedMilliseconds % (1000 \* 60 \* 60)) / (1000 \* 60));     return `${elapsedDays} días, ${elapsedHours} horas y ${elapsedMinutes} minutos`;  }Una vez hecho esto, procedemos a implementar el algoritmo de transformación en el pipe. Este algoritmo calculará el tiempo transcurrido desde una fecha específica hasta el día de hoy, utilizando el objeto `Date` de JavaScript y aplicando algunas operaciones matemáticas. ```ts transform(value: string): string { const currentDate = new Date(); const receiptDate = new Date(value) const elapsedMilliseconds = currentDate.getTime() - receiptDate.getTime(); const elapsedDays = Math.floor(elapsedMilliseconds / (1000 * 60 * 60 * 24)); const elapsedHours = Math.floor((elapsedMilliseconds % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); const elapsedMinutes = Math.floor((elapsedMilliseconds % (1000 * 60 * 60)) / (1000 * 60)); return `${elapsedDays} días, ${elapsedHours} horas y ${elapsedMinutes} minutos`; } ``` `const currentDate = new Date();` -> Obtenemos la fecha actual `const receiptDate = new Date(value)` -> Convertimos la fecha recibida al tipo `Date`. `const elapsedMilliseconds = currentDate.getTime() - receiptDate.getTime();` -> Calcula la diferencia en milisegundos entre la fecha actual y la fecha dada. `elapsedDays, elapsedHours, elapsedMinutes` -> Convierte la diferencia en días, horas y minutos usando matemáticas básicas.
Esta es mi solución de time-ago fue utilizando un stack, aunque fue una solución muy extensa la verdad. Este es mi codigo. ```js import { Pipe, PipeTransform } from '@angular/core'; interface StackNode { next(): StackNode, isNext() : Boolean, get(): String } enum TimeAgoEnum { SECONDS = 1000, MINUTES = 60, HOURS = 60, DAYS = 24, WEEK = 7, MONTH = 4 } enum TimeLimitEnum { SECONDS = 60, MINUTES = 60, HOURS = 24, DAYS = 7, WEEK = 4, MONTH = 12 } interface Data { v: TimeAgoEnum, t: 'Just Now' | 'Minutes' | 'Hours' | 'Days' | 'Weeks' | 'Months', l: TimeLimitEnum, } class TimeAgo implements StackNode { params: Data[] = [ { v: TimeAgoEnum.SECONDS, t: 'Just Now', l: TimeLimitEnum.SECONDS}, { v: TimeAgoEnum.MINUTES, t: 'Minutes', l: TimeLimitEnum.MINUTES}, { v: TimeAgoEnum.HOURS, t: 'Hours', l: TimeLimitEnum.HOURS}, { v: TimeAgoEnum.DAYS, t: 'Days', l: TimeLimitEnum.DAYS}, { v: TimeAgoEnum.WEEK, t: 'Weeks', l: TimeLimitEnum.WEEK}, { v: TimeAgoEnum.MONTH, t: 'Months', l: TimeLimitEnum.MONTH} ] idxParams = -1; createdAt! : number; diffTime! : number; shouldContinue = true; resultTimeAgo = ""; constructor(createdAt: number) { this.createdAt = createdAt; this.diffTime = Math.floor(Date.now() - createdAt); } next(): StackNode { if (this.shouldContinue == false) throw new Error('Cannot next') const currenParam = this.params[this.idxParams]; const compare = Math.floor(this.diffTime / currenParam.v.valueOf()); if (compare <= 0 || compare < currenParam.l.valueOf()) this.shouldContinue = false; if (compare > 0) this.diffTime = compare; this.resultTimeAgo = `${this.diffTime} ${currenParam.t} ago!`.toLowerCase(); return this; } isNext(): Boolean { this.idxParams += 1; if (this.idxParams == this.params.length) return false; return this.shouldContinue; } get(): string { return this.resultTimeAgo; } } @Pipe({ name: 'timeAgo', standalone: true }) export class TimeAgoPipe implements PipeTransform { transform(value: string, ...args: string[]): string { console.log(`${value}`); const timeAgo = new TimeAgo(Number(value)) while (timeAgo.isNext()) timeAgo.next(); return timeAgo.get(); } } ```import { Pipe, PipeTransform } from '@angular/core'; *interface* <u>StackNode</u> { next(): <u>StackNode</u>, isNext() : <u>Boolean</u>, get(): <u>String</u>} *enum* <u>TimeAgoEnum</u> { SECONDS = 1000, MINUTES = 60, HOURS = 60, DAYS = 24, WEEK = 7, MONTH = 4} *enum* <u>TimeLimitEnum</u> { SECONDS = 60, MINUTES = 60, HOURS = 24, DAYS = 7, WEEK = 4, MONTH = 12} *interface* <u>Data</u> { v: <u>TimeAgoEnum</u>, t: 'Just Now' | 'Minutes' | 'Hours' | 'Days' | 'Weeks' | 'Months', l: <u>TimeLimitEnum</u>,} *class* <u>TimeAgo</u> implements *<u>StackNode</u>* { params: <u>Data</u>\[] = \[ { v: TimeAgoEnum.SECONDS, t: 'Just Now', l: TimeLimitEnum.SECONDS}, { v: TimeAgoEnum.MINUTES, t: 'Minutes', l: TimeLimitEnum.MINUTES}, { v: TimeAgoEnum.HOURS, t: 'Hours', l: TimeLimitEnum.HOURS}, { v: TimeAgoEnum.DAYS, t: 'Days', l: TimeLimitEnum.DAYS}, { v: TimeAgoEnum.WEEK, t: 'Weeks', l: TimeLimitEnum.WEEK}, { v: TimeAgoEnum.MONTH, t: 'Months', l: TimeLimitEnum.MONTH} ] idxParams = -1; createdAt! : *number*; diffTime! : *number*; shouldContinue = true; resultTimeAgo = ""; *constructor*(*createdAt*: *number*) { this.createdAt = createdAt; this.diffTime = Math.floor(Date.now() - createdAt); } next(): <u>StackNode</u> { if (this.shouldContinue == false) throw new Error('Cannot next') *const* currenParam = this.params\[this.idxParams]; *const* compare = Math.floor(this.diffTime / currenParam.v.valueOf()); if (compare <= 0 || compare < currenParam.l.valueOf()) this.shouldContinue = false; if (compare > 0) this.diffTime = compare; this.resultTimeAgo = `${this.diffTime} ${currenParam.t} ago!`.toLowerCase(); return this; } isNext(): <u>Boolean</u> { this.idxParams += 1; if (this.idxParams == this.params.length) return false; return this.shouldContinue; } get(): *string* { return this.resultTimeAgo; }} @Pipe({ name: 'timeAgo', standalone: true})export *class* <u>TimeAgoPipe</u> implements *<u>PipeTransform</u>* { transform(*value*: *string*, ...*args*: *string*\[]): *string* { console.log(`${value}`); *const* timeAgo = new TimeAgo(Number(value)) while (timeAgo.isNext()) timeAgo.next(); return timeAgo.get(); } }
Documentacion de pipes [Angular - Transforming Data Using Pipes](https://angular.io/guide/pipes)