Pipes — formatowanie w szablonie
W klasie trzymasz liczbę 12.5 i datę jako obiekt Date. Użytkownik chce zobaczyć „12,50 zł” i „piątek, 16 października”. Do takiej zamiany — tylko na ekranie, bez ruszania danych — służą pipes. Przy okazji raz na zawsze włączysz w aplikacji polskie formaty.
Czego się dziś nauczysz
- Sformatujesz wartość w szablonie zapisem
{{ wartosc | pipe }}i zaimportujesz pipe do komponentu. - Zmienisz wielkość liter pipe’ami
uppercase,lowercase,titlecasei wytniesz fragment przezslice. - Włączysz w
app.config.tspolskie ustawienia regionalne. - Pokażesz datę w wybranym formacie pipe’em
datei odróżniszMModmm. - Pokażesz liczbę, kwotę i procent po polsku pipe’ami
number,currency,percent. - Połączysz kilka pipe’ów w łańcuch i przekażesz im parametry.
- Podejrzysz surowe dane pipe’em
json.
Przygotowanie: lekcje 05 i 09–13 — znasz interpolację, sygnały, computed i @for. Przewidywany czas: 45 min z zadaniami.
Dane to jedno, wygląd to drugie
Kwota 12.5 to dobra wartość do liczenia, ale źle wygląda na ekranie: brakuje zera, waluty, a Polak spodziewa się przecinka. toFixed(2) z lekcji 05 daje 12.50, ale z kropką i bez „zł”. Mógłbyś napisać metodę, która zamienia liczbę na ładny tekst… i drugą dla dat, i trzecią dla procentów.
Angular ma to wbudowane. W szablonie piszesz {{ cena() | currency:'PLN' }} — dane zostają liczbą, a na ekranie pojawia się sformatowany tekst. Kreska | to pipe (po polsku czasem „potok”).
| Zapis | Znaczenie |
|---|---|
{{ x | uppercase }} | wartość przechodzi przez pipe, na ekranie jest wynik |
{{ data | date:'dd.MM.yyyy' }} | parametr po dwukropku |
{{ tekst | slice:0:10 }} | kilka parametrów — kolejne dwukropki |
{{ tytul | slice:0:12 | uppercase }} | łańcuch: wynik pierwszego trafia do drugiego |
Każdy pipe trzeba zaimportować
Pipe’y to klasy z @angular/common, które dopisujesz do imports komponentu: UpperCasePipe, LowerCasePipe, TitleCasePipe, SlicePipe, DatePipe, DecimalPipe (w szablonie: number), CurrencyPipe, PercentPipe, JsonPipe. Bez importu: błąd NG8004 No pipe found with name ….
Pipe niczego nie zmienia w klasie. cena() wciąż zwraca 12.5, którą możesz dodawać i mnożyć. Formatowanie dzieje się wyłącznie w chwili wyświetlania.
Pipe’y tekstowe
Na rozgrzewkę pipe’y, które nie zależą od kraju: wielkość liter i wycinanie fragmentu tekstu albo tablicy.
import { Component, signal } from '@angular/core';
import { LowerCasePipe, SlicePipe, TitleCasePipe, UpperCasePipe } from '@angular/common';
@Component({
selector: 'app-demo',
imports: [UpperCasePipe, LowerCasePipe, TitleCasePipe, SlicePipe],
templateUrl: './demo.html'
})
export class Demo {
readonly tytul = signal('wielki turniej gier retro w sali dwunastej');
readonly sala = signal('SALA INFORMATYCZNA');
readonly gracze = signal(['Pixel', 'Bajt', 'Kursor', 'Ping', 'Router']);
}
<div class="p-3">
<ul class="list-group" style="width: 32rem">
<li class="list-group-item">uppercase: {{ tytul() | uppercase }}</li>
<li class="list-group-item">titlecase: {{ tytul() | titlecase }}</li>
<li class="list-group-item">lowercase: {{ sala() | lowercase }}</li>
<li class="list-group-item">slice:0:15 — {{ tytul() | slice:0:15 }}…</li>
<li class="list-group-item">podium: {{ (gracze() | slice:0:3).join(', ') }}</li>
</ul>
</div>
uppercase: WIELKI TURNIEJ GIER RETRO W SALI DWUNASTEJ
titlecase: Wielki Turniej Gier Retro W Sali Dwunastej
lowercase: sala informatyczna
slice:0:15 — wielki turniej …
podium: Pixel, Bajt, Kursor
titlecase zaczyna każde słowo wielką literą. slice:0:15 bierze znaki od 0 do 14 — tak samo jak metoda slice w TypeScripcie. Na tablicy działa tak samo i zwraca pierwsze trzy elementy.
Zatrzymaj się
W ostatnim wierszu wokół gracze() | slice:0:3 są nawiasy. Co by się stało bez nich — do czego zostałoby przyczepione .join?
Polskie ustawienia i daty
Domyślnie Angular formatuje wszystko po amerykańsku: Oct 16, 2026. Żeby dostać 16 paź 2026, trzeba raz, dla całej aplikacji, włączyć polskie ustawienia regionalne w app.config.ts. Dochodzą trzy wiersze importów, wywołanie registerLocaleData i jeden wpis w providers.
import { ApplicationConfig, LOCALE_ID, provideBrowserGlobalErrorListeners } from '@angular/core';
import { provideRouter } from '@angular/router';
import { registerLocaleData } from '@angular/common';
import localePl from '@angular/common/locales/pl';
import { routes } from './app.routes';
registerLocaleData(localePl);
export const appConfig: ApplicationConfig = {
providers: [
provideBrowserGlobalErrorListeners(),
provideRouter(routes),
{ provide: LOCALE_ID, useValue: 'pl' }
]
};
@END
@@CODE nowa-16-2/demo.ts
@@CODE nowa-16-2/demo.html
@@OUT wynik w przeglądarce
bez parametru: 16 paź 2026
dd.MM.yyyy: 16.10.2026
HH:mm: 16:30
EEEE: piątek
d MMMM y: 16 października 2026
fullDate: piątek, 16 października 2026
Nazwy dni i miesięcy są po polsku i w dobrej odmianie — to zasługa danych localePl. W new Date(2026, 9, 16, 16, 30) miesiąc 9 to październik, bo miesiące w JavaScripcie liczy się od zera — częsty błąd na egzaminie.
| Znak | Znaczenie | Znak | Znaczenie |
|---|---|---|---|
d / dd | dzień: 6 / 06 | HH | godzina 0–23 |
M / MM | miesiąc: 3 / 03 | mm | minuty |
MMM / MMMM | paź / października | EEE / EEEE | pt. / piątek |
y / yyyy | rok | ss | sekundy |
Zatrzymaj się
Co pokaże date:'dd.mm.yyyy' z małymi mm? Sprawdź i wyjaśnij wynik.
Liczby, kwoty i procenty
Paragon ze sklepiku szkolnego. Trzy pipe’y liczbowe: number (format liczby), currency (kwota z walutą) i percent (ułamek jako procent). Polskie ustawienia z przykładu 2 muszą być włączone.
import { Component, computed, signal } from '@angular/core';
import { CurrencyPipe, DecimalPipe, PercentPipe } from '@angular/common';
@Component({
selector: 'app-demo',
imports: [DecimalPipe, CurrencyPipe, PercentPipe],
templateUrl: './demo.html'
})
export class Demo {
readonly cena = signal(12.5);
readonly sztuk = signal(3);
readonly rabat = signal(0.15);
readonly masa = signal(1234.5678);
readonly razem = computed(() => this.cena() * this.sztuk() * (1 - this.rabat()));
}
<div class="p-3">
<ul class="list-group" style="width: 30rem">
<li class="list-group-item">masa | number: {{ masa() | number }}</li>
<li class="list-group-item">masa | number:'1.0-0': {{ masa() | number:'1.0-0' }}</li>
<li class="list-group-item">masa | number:'1.2-2': {{ masa() | number:'1.2-2' }}</li>
<li class="list-group-item">cena | currency:'PLN': {{ cena() | currency:'PLN' }}</li>
<li class="list-group-item">rabat | percent: {{ rabat() | percent }}</li>
<li class="list-group-item fw-bold">do zapłaty: {{ razem() | currency:'PLN' }}</li>
</ul>
</div>
masa | number: 1 234,568
masa | number:'1.0-0': 1 235
masa | number:'1.2-2': 1 234,57
cena | currency:'PLN': 12,50 zł
rabat | percent: 15%
do zapłaty: 31,88 zł
Zapis '1.2-2' czyta się: co najmniej 1 cyfra przed przecinkiem, od 2 do 2 cyfr po przecinku. '1.0-1' to „najwyżej jedna cyfra po przecinku”. Liczba jest zaokrąglana, nie obcinana. Zwróć uwagę na polski przecinek, odstęp między tysiącami i walutę za liczbą.
Zatrzymaj się
percent pokazuje 0.15 jako 15%. Co pokaże dla wartości 15? Dlaczego?
Łańcuch pipe’ów i podgląd danych przez json
Lista wydarzeń z datą i ceną. Tytuł skracamy i zamieniamy na wielkie litery jednocześnie. Na dole json pokazuje surowe dane — to narzędzie do szukania błędów, a nie do pokazywania użytkownikowi.
import { Component, signal } from '@angular/core';
import { CurrencyPipe, DatePipe, JsonPipe, SlicePipe, UpperCasePipe } from '@angular/common';
@Component({
selector: 'app-demo',
imports: [DatePipe, CurrencyPipe, SlicePipe, UpperCasePipe, JsonPipe],
templateUrl: './demo.html'
})
export class Demo {
readonly wydarzenia = signal([
{ id: 1, tytul: 'Turniej gier retro', data: new Date(2026, 9, 16), cena: 5 },
{ id: 2, tytul: 'Noc robotów i automatyki', data: new Date(2026, 10, 6), cena: 0 },
{ id: 3, tytul: 'Warsztat druku 3D dla początkujących', data: new Date(2026, 11, 4), cena: 12.5 },
]);
}
<div class="p-3">
<table class="table" style="width: 36rem">
<thead><tr><th>Wydarzenie</th><th>Kiedy</th><th>Wstęp</th></tr></thead>
<tbody>
@for (w of wydarzenia(); track w.id) {
<tr>
<td>{{ w.tytul | slice:0:16 | uppercase }}</td>
<td>{{ w.data | date:'EEE, d MMM' }}</td>
<td>
@if (w.cena === 0) { bezpłatny } @else { {{ w.cena | currency:'PLN' }} }
</td>
</tr>
}
</tbody>
</table>
<pre class="bg-light p-2 small" style="width: 36rem">{{ wydarzenia()[0] | json }}</pre>
</div>
TURNIEJ GIER RET pt., 16 paź 5,00 zł
NOC ROBOTÓW I AU pt., 6 lis bezpłatny
WARSZTAT DRUKU 3 pt., 4 gru 12,50 zł
{
"id": 1,
"tytul": "Turniej gier retro",
"data": "2026-10-15T22:00:00.000Z",
"cena": 5
}
W ramce data jest zapisana w strefie UTC — 2026-10-15T22:00:00.000Z to wciąż północ 16 października w Polsce, tylko liczona od południka zero.
Zatrzymaj się
Zamień kolejność na uppercase | slice:0:16. Czy wynik się zmieni? W jakiej sytuacji kolejność pipe’ów miałaby znaczenie?
Zestawienie elementów
| Pipe (w szablonie) | Klasa do importu | Przykład → wynik |
|---|---|---|
uppercase, lowercase | UpperCasePipe, LowerCasePipe | 'Ala' | uppercase → ALA |
titlecase | TitleCasePipe | 'jan kowalski' → Jan Kowalski |
slice:od:do | SlicePipe | tekst albo tablica |
date:'format' | DatePipe | 'dd.MM.yyyy' → 16.10.2026 |
number:'1.2-2' | DecimalPipe | 1234.5 → 1 234,50 |
currency:'PLN' | CurrencyPipe | 12.5 → 12,50 zł |
percent | PercentPipe | 0.15 → 15% |
json | JsonPipe | obiekt jako tekst — do szukania błędów |
registerLocaleData(localePl) | wczytanie polskich nazw i formatów — w app.config.ts | |
{ provide: LOCALE_ID, useValue: 'pl' } | polski jako domyślny język formatów — w providers | |
Zanim utkniesz
| Zapis | Problem |
|---|---|
{{ cena() | currency:'PLN' }} bez importu | NG8004: No pipe found with name 'currency’. Dodaj CurrencyPipe do imports. |
LOCALE_ID ustawiony na 'pl' bez registerLocaleData(localePl) | Biała strona i błąd w konsoli: Missing locale data for the locale „pl”. Potrzebne są obie rzeczy. |
date:'dd.mm.yyyy' | Zamiast miesiąca pojawiają się minuty (najczęściej 00). Miesiąc to wielkie MM. |
new Date(2026, 10, 16) jako 16 października | To 16 listopada — miesiące liczone od zera. |
{{ cena() | number:2 }} | Błąd typu: parametr number to tekst w apostrofach, np. '1.2-2'. |
Formatowanie w klasie: cena = '12,50 zł' | Na ekranie wygląda dobrze, ale nie da się tego dodawać ani mnożyć. Trzymaj liczby jako liczby, formatuj pipe’em. |
{{ cena | currency:'PLN' }} przy sygnale | Ostrzeżenie NG8109 i błąd typu — pipe dostał sygnał zamiast liczby. Dopisz (). |
Zadania
Sygnały z imieniem i nazwiskiem zapisanymi małymi literami. Wyświetl imię przez titlecase, a nazwisko przez uppercase, np. „Jan KOWALSKI”.
Kryterium sukcesu: niezależnie od tego, jak zapiszesz dane w kodzie (małe, wielkie, mieszane), na ekranie wygląda to tak samo.
Podpowiedź
Dwa pipe’y, dwa importy.
Tablica pięciu produktów sklepiku { id, nazwa, cena } w tabeli. Ceny przez currency:'PLN', nazwy przez titlecase, a pod tabelą najdroższy produkt i średnia cena z dwoma miejscami po przecinku.
Kryterium sukcesu: wszystkie kwoty mają polski przecinek i „zł”, a średnia zgadza się z rachunkiem.
Podpowiedź
Średnia i najdroższy produkt to computed; średnią pokaż przez number:'1.2-2'.
Tablica wydarzeń z datami (obiekty Date). Pokaż listę z datą w formacie „pt., 16.10” i godziną „16:30” w osobnej kolumnie oraz pełną datą w dymku ([title]) w formacie fullDate. Wydarzenia, które już minęły, mają być szare.
Kryterium sukcesu: daty są po polsku, dymek pokazuje pełną datę, a przeszłe wydarzenia są wyszarzone.
Podpowiedź
Pipe działa też w wiązaniu: [title]="w.data | date:'fullDate'". „Minęło”: w.data < dzis, gdzie dzis = new Date() to pole klasy.
Pokaż dzisiejszą datę w formacie „czwartek, 1 października 2026” oraz liczbę dni do 26 czerwca przyszłego roku i liczbę godzin do tego dnia z odstępem między tysiącami (np. „6 384”).
Kryterium sukcesu: data jest po polsku z pełną nazwą dnia i miesiąca, a liczby dni i godzin zgadzają się z kalendarzem.
Podpowiedź
Różnica dwóch dat w milisekundach: koniec.getTime() - dzis.getTime(); dni: podziel przez 1000 * 60 * 60 * 24 i zaokrąglij Math.ceil. Odstęp da pipe number.
Lista trzech produktów (nazwa, cena, liczba sztuk) z przyciskami ± przy każdym. Pokaż wartość każdej pozycji, sumę koszyka i — gdy suma przekroczy 100 zł — rabat 10% oraz kwotę po rabacie. Wszystkie kwoty przez currency:'PLN', a stawkę rabatu przez percent.
Kryterium sukcesu: kwoty są po polsku (przecinek, zł), suma i rabat przeliczają się same po każdej zmianie.
Podpowiedź
Suma i rabat to computed. Zmiana liczby sztuk jednego produktu: map z kopią obiektu, jak w lekcji 10.
Zrób sygnał z obiektem zgłoszenia { imie, nazwisko, klasa, dataZgloszenia, oplata } i trzy przyciski, które zmieniają po jednym polu. Pokaż zgłoszenie dwa razy obok siebie: po lewej „dla użytkownika” (sformatowane pipe’ami: titlecase, uppercase, date, currency), po prawej „dla programisty” (json w znaczniku <pre>). Sprawdź, że po każdej zmianie obie strony się zgadzają.
Kryterium sukcesu: obie kolumny zmieniają się razem, lewa jest czytelna dla człowieka, prawa pokazuje surowe dane.
Podpowiedź
Dwie kolumny: row i dwa col-md-6. Zmiana pola: update(z => ({ ...z, oplata: z.oplata + 5 })).
Co trzeba zapamiętać
{{ wartosc | pipe:parametr }}formatuje dane tylko na ekranie; wartość w klasie się nie zmienia.- Każdy pipe importujesz z
@angular/commondoimportskomponentu. - Polskie daty i kwoty wymagają
registerLocaleData(localePl)iLOCALE_IDwapp.config.ts. - W formacie daty
MMto miesiąc,mm— minuty; miesiące wnew Dateliczy się od zera. - Pipe’y można łączyć: wynik jednego trafia do następnego.
jsonsłuży do podglądu danych podczas szukania błędów.
Sprawdź się: na ekranie zamiast 16.10.2026 pojawia się 16.30.2026. Jaki znak w formacie daty jest zły?
Dokumentacja: angular.dev — pipes.