Przejdź do treści
School IT / Przestrzeń do nauki

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.

Angular 22 uppercase slice date number currency percent json LOCALE_ID 45 min
CEL LEKCJI

Czego się dziś nauczysz

  • Sformatujesz wartość w szablonie zapisem {{ wartosc | pipe }} i zaimportujesz pipe do komponentu.
  • Zmienisz wielkość liter pipe’ami uppercase, lowercase, titlecase i wytniesz fragment przez slice.
  • Włączysz w app.config.ts polskie ustawienia regionalne.
  • Pokażesz datę w wybranym formacie pipe’em date i odróżnisz MM od mm.
  • 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.

TEORIA

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”).

ZapisZnaczenie
{{ 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.

PRZYKŁAD 1

Pipe’y tekstowe

Na rozgrzewkę pipe’y, które nie zależą od kraju: wielkość liter i wycinanie fragmentu tekstu albo tablicy.

src/app/nowa-16-1/demo.ts — pełny plik
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']);
}
src/app/nowa-16-1/demo.html — pełny plik
<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>
wynik w przeglądarce
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?

PRZYKŁAD 2

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.

src/app/app.config.ts — pełny plik
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.

ZnakZnaczenieZnakZnaczenie
d / dddzień: 6 / 06HHgodzina 0–23
M / MMmiesiąc: 3 / 03mmminuty
MMM / MMMMpaź / październikaEEE / EEEEpt. / piątek
y / yyyyroksssekundy

Zatrzymaj się

Co pokaże date:'dd.mm.yyyy' z małymi mm? Sprawdź i wyjaśnij wynik.

PRZYKŁAD 3

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.

src/app/nowa-16-3/demo.ts — pełny plik
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()));
}
src/app/nowa-16-3/demo.html — pełny plik
<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>
wynik w przeglądarce
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?

PRZYKŁAD 4

Ł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.

src/app/nowa-16-4/demo.ts — pełny plik
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 },
    ]);
}
src/app/nowa-16-4/demo.html — pełny plik
<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>
wynik w przeglądarce
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?

ELEMENTY WBUDOWANE

Zestawienie elementów

Pipe (w szablonie)Klasa do importuPrzykład → wynik
uppercase, lowercaseUpperCasePipe, LowerCasePipe'Ala' | uppercase → ALA
titlecaseTitleCasePipe'jan kowalski' → Jan Kowalski
slice:od:doSlicePipetekst albo tablica
date:'format'DatePipe'dd.MM.yyyy' → 16.10.2026
number:'1.2-2'DecimalPipe1234.5 → 1 234,50
currency:'PLN'CurrencyPipe12.5 → 12,50 zł
percentPercentPipe0.15 → 15%
jsonJsonPipeobiekt 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
CZĘSTE BŁĘDY

Zanim utkniesz

ZapisProblem
{{ cena() | currency:'PLN' }} bez importuNG8004: 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ździernikaTo 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 sygnaleOstrzeżenie NG8109 i błąd typu — pipe dostał sygnał zamiast liczby. Dopisz ().
ZADANIA

Zadania

ZAD 1Wizytówka★☆☆

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.

ZAD 2Cennik po polsku★☆☆

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'.

ZAD 3Plan wydarzeń★★☆

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.

ZAD 4Do wakacji★★☆

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.

ZAD 5Koszyk★★★

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.

ZAD 6Raport z json★★★

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 })).

PODSUMOWANIE

Co trzeba zapamiętać

  • {{ wartosc | pipe:parametr }} formatuje dane tylko na ekranie; wartość w klasie się nie zmienia.
  • Każdy pipe importujesz z @angular/common do imports komponentu.
  • Polskie daty i kwoty wymagają registerLocaleData(localePl) i LOCALE_ID w app.config.ts.
  • W formacie daty MM to miesiąc, mm — minuty; miesiące w new Date liczy się od zera.
  • Pipe’y można łączyć: wynik jednego trafia do następnego.
  • json sł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.

Postęp zapisuje się w tej przeglądarce.