PipesPipes w Angularze – transformacja danych

1. Czym są Pipes?

Pipes, znane również jako filtry, to funkcjonalność Angulara, która pozwala na transformację danych wyświetlanych w szablonach aplikacji, bez zmiany samej formy tych danych. Pipes są używane do formatowania danych bezpośrednio w szablonach HTML, co czyni je niezwykle przydatnym narzędziem w tworzeniu interfejsów użytkownika.

Analogia z życia:

Pipe = Filtr do kawy ☕

  • Kawa (dane oryginalne) → Filtr (pipe) → Czysta kawa (sformatowane dane)
  • Kawa pozostaje taka sama, tylko zostaje przefiltrowvana do lepszej prezentacji

W aplikacji filmowej:

Data: "2023-10-15" → DatePipe → "15 października 2023"
Cena: 25.99 → CurrencyPipe → "25,99 zł"
Tytuł: "avengers" → TitleCasePipe → "Avengers"

2. Rodzaje Pipes w Angularze

Angular oferuje kilka wbudowanych pipes, które mogą być używane do różnych zadań:

  • DatePipe – formatowanie dat
  • UpperCasePipe i LowerCasePipe – zmiana wielkości liter tekstu
  • TitleCasePipe – pierwsza litera każdego słowa wielka
  • DecimalPipe – formatowanie liczb
  • CurrencyPipe – formatowanie wartości pieniężnych
  • PercentPipe – formatowanie procentów
  • JsonPipe – wyświetlanie obiektów jako JSON
  • SlicePipe – wycinanie fragmentów

3. Przykład użycia pipe w komponencie (Twój kod)

Definicja komponentu

// product.component.ts
import { Component, Input } from '@angular/core';
import { CommonModule, CurrencyPipe } from '@angular/common';

@Component({
  selector: 'app-product',
  standalone: true,
  imports: [CommonModule],
  template: `<div>Nazwa: {{ name }} <br> Cena: {{ price | currency:'USD' }}</div>`,
  providers: [CurrencyPipe]
})
export class ProductComponent {
  @Input() name: string;
  @Input() price: number;
}

Użycie komponentu

<!-- Wykorzystanie w innym komponencie lub template -->
<app-product name="Laptop" price="799"></app-product>

Wyjaśnienie przykładu

  • CurrencyPipe: W przykładzie użyliśmy CurrencyPipe do formatowania ceny produktu jako wartości walutowej w dolarach amerykańskich (USD). Pipe jest użyty bezpośrednio w szablonie, co oznacza, że wartość price jest transformowana na sformatowaną cenę walutową, gdy jest renderowana w DOM.
  • Standalone Component: Komponent ProductComponent jest zdefiniowany jako komponent standalone, co oznacza, że nie wymaga on deklaracji w żadnym NgModule. Wystarczy zaimportować CommonModule i użyć CurrencyPipe jako dostarczonego dostawcy (provider).

4. Praktyczne przykłady z aplikacją filmową

Przykład 1: Formatowanie daty premiery

// film-card.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-film-card',
  standalone: true,
  imports: [CommonModule],
  template: `
    <div class="film-card">
      <h3>{{ film.title | titlecase }}</h3>
      <p>Premiera: {{ film.releaseDate | date:'dd/MM/yyyy' }}</p>
      <p>Czas trwania: {{ film.duration }} min</p>
    </div>
  `
})
export class FilmCardComponent {
  film = {
    title: 'avengers: endgame',
    releaseDate: new Date('2019-04-26'),
    duration: 181
  };
}

Rezultat:

  • avengers: endgameAvengers: Endgame
  • 2019-04-2626/04/2019

Przykład 2: Formatowanie cen biletów

// ticket-price.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-ticket-price',
  standalone: true,
  imports: [CommonModule],
  template: `
    <div class="pricing">
      <h4>Cennik biletów</h4>
      <p>Normalny: {{ prices.normal | currency:'PLN':'symbol':'1.2-2' }}</p>
      <p>Ulgowy: {{ prices.reduced | currency:'PLN' }}</p>
      <p>Zniżka: {{ discount | percent }}</p>
    </div>
  `
})
export class TicketPriceComponent {
  prices = {
    normal: 25.99,
    reduced: 18.50
  };
  discount = 0.15; // 15%
}

Rezultat:

  • 25.9925,99 zł
  • 18.5018,50 zł
  • 0.1515%

Przykład 3: Formatowanie ocen filmów

// film-rating.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-film-rating',
  standalone: true,
  imports: [CommonModule],
  template: `
    <div class="ratings">
      <h4>{{ film.title | uppercase }}</h4>
      <p>Ocena: {{ film.rating | number:'1.1-1' }}/10</p>
      <p>Budget: {{ film.budget | currency:'USD':'symbol':'1.0-0' }}</p>
      <p>Opis: {{ film.description | slice:0:50 }}...</p>
    </div>
  `
})
export class FilmRatingComponent {
  film = {
    title: 'Spider-Man',
    rating: 8.567,
    budget: 200000000,
    description: 'Peter Parker zostaje ukąszony przez radioaktywnego pająka...'
  };
}

Rezultat:

  • Spider-ManSPIDER-MAN
  • 8.5678.6
  • 200000000$200,000,000
  • Długi opis → Peter Parker zostaje ukąszony przez radioaktywnego...

Przykład 4: Kombinowanie pipes

// film-info.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-film-info',
  standalone: true,
  imports: [CommonModule],
  template: `
    <div class="film-info">
      <h3>{{ film.title | titlecase }}</h3>
      
      <!-- Kombinowanie pipes -->
      <p>Dodano: {{ film.addedDate | date:'shortDate' | uppercase }}</p>
      
      <!-- Pipe z parametrami -->
      <p>Reżyser: {{ film.director | slice:0:20 | titlecase }}</p>
      
      <!-- Warunkowe pipes -->
      <p>Status: {{ film.isAvailable ? 'DOSTĘPNY' : 'NIEDOSTĘPNY' | lowercase }}</p>
      
      <!-- JSON pipe do debugowania -->
      <details>
        <summary>Debug info</summary>
        <pre>{{ film | json }}</pre>
      </details>
    </div>
  `
})
export class FilmInfoComponent {
  film = {
    title: 'the dark knight',
    addedDate: new Date(),
    director: 'christopher nolan',
    isAvailable: true,
    rating: 9.0,
    genre: 'action'
  };
}

Łańcuchowanie pipes (pipe chaining)

<!-- Można łączyć kilka pipes razem -->
<p>{{ userName | lowercase | titlecase }}</p>
<p>{{ price | currency:'EUR' | uppercase }}</p>
<p>{{ longText | slice:0:30 | titlecase }}</p>

Przykład:

export class ChainExample {
  userName = 'JOHN DOE';
  longText = 'very long movie description here';
}
<p>{{ userName | lowercase | titlecase }}</p>
<!-- JOHN DOE → john doe → John Doe -->

6. Pipes z parametrami

Składnia podstawowa:

// parameters-example.component.ts
export class ParametersExampleComponent {
  filmDate = new Date('2023-12-25');
  price = 29.99;
  rating = 0.87;
}
<!-- Date pipe z parametrami -->
<p>Data: {{ filmDate | date:'dd-MM-yyyy' }}</p>
<p>Pełna data: {{ filmDate | date:'fullDate':'pl' }}</p>

<!-- Currency pipe z parametrami -->
<p>Cena: {{ price | currency:'PLN':'symbol':'1.2-2' }}</p>

<!-- Number pipe z parametrami -->
<p>Ocena: {{ rating | percent:'1.0-1' }}</p>

<!-- Slice pipe z parametrami -->
<p>Fragment: {{ 'Bardzo długi tekst filmu' | slice:0:10 }}</p>

7. Przydatne kombinacje pipes

Formatowanie listy aktorów:

<p>Główne role: {{ film.actors | slice:0:3 | json }}</p>

Formatowanie dat w różnych formatach:

<p>Krótka data: {{ date | date:'shortDate' }}</p>
<p>Długa data: {{ date | date:'longDate' }}</p>
<p>Tylko rok: {{ date | date:'yyyy' }}</p>
<p>Własny format: {{ date | date:'dd.MM.yyyy HH:mm' }}</p>

Formatowanie liczb:

<p>Zwykła: {{ number | number }}</p>
<p>2 miejsca: {{ number | number:'1.2-2' }}</p>
<p>Procent: {{ number | percent:'1.1-1' }}</p>

8. Najczęstsze zastosowania

✅ Kiedy używać pipes:

  1. Formatowanie dat – wyświetlanie dat premiery
  2. Formatowanie cen – ceny biletów, budżety filmów
  3. Formatowanie tekstu – tytuły, opisy
  4. Debugowanie – JsonPipe do sprawdzania danych
  5. Skracanie tekstu – slice dla długich opisów

❌ Kiedy NIE używać pipes:

  1. Złożone obliczenia – lepiej użyć metod w komponencie
  2. Modyfikacja oryginalnych danych – pipes nie zmieniają źródła
  3. Asynchroniczne operacje – użyj async pipe

9. Najlepsze praktyki

✅ Co robić:

  1. Używaj wbudowanych pipes gdy to możliwe
  2. Łącz pipes w logicznej kolejności
  3. Dodawaj parametry dla precyzji (np. format daty)
  4. Używaj JsonPipe do debugowania

❌ Czego unikać:

  1. Za dużo pipes w jednej linii – utrudnia czytanie
  2. Pipes do modyfikacji danych – tylko do prezentacji
  3. Złożonej logiki w pipes – lepiej w komponencie

Przykład dobrej praktyki:

// ✅ Dobra praktyka
export class GoodPracticeComponent {
  film = {
    title: 'avengers',
    price: 25.99,
    date: new Date()
  };
}
<!-- ✅ Czytelne i logiczne -->
<h3>{{ film.title | titlecase }}</h3>
<p>Cena: {{ film.price | currency:'PLN' }}</p>
<p>Data: {{ film.date | date:'shortDate' }}</p>

11. Ćwiczenia do wykonania

  1. Cennik kina – stwórz komponent z różnymi pipes dla cen
  2. Lista filmów – formatuj tytuły, daty, oceny
  3. Profil użytkownika – formatuj dane osobowe różnymi pipes
  4. Statystyki – używaj number i percent pipes