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.
Decoradores en Angular:
Un pipe en Angular utiliza el decorador @Pipe, que define su metadato principal, incluido su nombre.
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:
¿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:
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.
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!
 transform(value:string):string{ const date =newDate(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){ returnthis.applyFormat(diffInHours,'hour'); } const diffInDays =Math.floor(diffInHours /24); if(diffInDays <30){ returnthis.applyFormat(diffInDays,'day'); } const diffInMonths =Math.floor(diffInDays /30); if(diffInMonths <12){ returnthis.applyFormat(diffInDays,'month'); } const diffInYears =Math.floor(diffInMonths /12); returnthis.applyFormat(diffInYears,'year'); }}
Reto:
transform(value: string): string {
const dates= value ? new Date(value) : new Date();
const now = new Date();
const months = now.getMonth() - dates.getMonth();
const days = now.getDate() - dates.getDate();
const years = now.getFullYear() - dates.getFullYear();
if (days > 30) {
if (months > 12) {
return ${years} years ago;
}
return ${months} months ago;
} else {
return ${days} days ago;
}
}
Que bueno agregarle horas!
Muy bien!
Hice esta función un poco larga pero funciona
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';
  }  
  }
}
probe el pipe con el texto: "Anita lava la tina", pero no me funciono el reverse
import{Pipe,PipeTransform}from'@angular/core';@Pipe({name:'timeAgo',})exportclassTimeAgoPipeimplementsPipeTransform{transform(value: any): string {if(!value)return'';const date =newDate(value);const now =newDate();const seconds =Math.floor((now.getTime()- date.getTime())/1000);if(seconds <60)return'Publicado hace un momento';const minutes =Math.floor(seconds /60);if(minutes <60)return`Publicado hace ${minutes} minutos`;const hours =Math.floor(minutes /60);if(hours <24)return`Publicado hace ${hours} hora(s)`;const days =Math.floor(hours /24);if(days <30)return`Publicado hace ${days} días`;return date.toLocaleDateString('es-CO',{year:'numeric',month:'long',day:'numeric'});}}
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'timeAgo',
})
export class TimeAgoPipe implements PipeTransform {
Adicionalmente cree un archivo de testing para comprobarlo time-ago.pipe.ts:
import{TimeAgoPipe}from'./time-ago.pipe';describe('TimeAgoPipe',()=>{letpipe:TimeAgoPipe;beforeEach(()=>{ pipe =newTimeAgoPipe();});it('should return "Just now" for the current time',()=>{const result = pipe.transform(newDate());expect(result).toBe('Just now.');});it('should return "1 Hour ago." for 1 hour ago',()=>{const oneHourAgo =newDate(); 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 =newDate(); 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 =newDate(); 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 =newDate(); 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 =newDate(); 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 =newDate(); 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 =newDate(); 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 =newDate(); 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})exportclassTimeAgoPipeimplementsPipeTransform{ transform(value:Date| string | number): string {const now =newDate();const inputDate =newDate(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:
{{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 =newDate();const receiptDate =newDate(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.