Wiązanie właściwości [ ]
Klamry wstawiają tekst. Nawiasy kwadratowe robią coś innego: przekazują wartość do właściwości elementu. Przy napisach różnicy nie widać. Przy disabled widać ją natychmiast — i od tej lekcji wygląd strony zacznie zależeć od danych.
Czego się dziś nauczysz
- Przekażesz wartość z klasy do właściwości elementu zapisem
[właściwość]="wyrażenie". - Wyjaśnisz, dlaczego
disabled="{{ x }}"blokuje przycisk także przyfalse. - Odróżnisz stały atrybut
title="…"od wiązania[title]="…". - Włączysz i wyłączysz pojedynczą klasę Bootstrapa zapisem
[class.nazwa]. - Ustawisz pojedynczy styl z jednostką zapisem
[style.width.rem]. - Ustawisz atrybut bez odpowiednika we właściwościach zapisem
[attr.nazwa]. - Rozpoznasz błąd składni przy połączeniu
[ ]i{{ }}.
Przygotowanie: lekcja 05 — umiesz pokazać wartość pola i użyć operatora ?:. Przewidywany czas: 45 min z zadaniami.
Tekst to nie to samo co wartość
Interpolacja zawsze daje tekst. Jeśli w polu jest liczba 0, w klamrach pojawi się napis "0". Jeśli wartość logiczna false — napis "false".
Dla akapitu to bez znaczenia, bo akapit i tak pokazuje tekst. Ale właściwość disabled pyta o prawdę lub fałsz, a napis "false" jest z jej punktu widzenia prawdziwy — bo jest niepusty. Stąd klasyczny błąd: przycisk zablokowany mimo false.
[właściwość]=”wyrażenie”
Nawiasy kwadratowe znaczą: „to, co w cudzysłowie, jest kodem, nie tekstem”. Angular oblicza wyrażenie i wynik — liczbę, wartość logiczną, tekst — przekazuje do właściwości elementu. W cudzysłowie wolno to samo, co w klamrach z lekcji 05: pola, obliczenia, metody, operator ?:.
<button title="Zapisz się">…</button> <!-- stały tekst "Zapisz się" -->
<button [title]="opis">…</button> <!-- wartość pola opis -->
<button [title]="'Zapisz się'">…</button> <!-- stały tekst jako wyrażenie: apostrofy w środku -->
Pytanie kontrolne
Zanim napiszesz wiązanie, zapytaj: „czy ta właściwość oczekuje tekstu?”. Jeśli tak (title, src, alt, placeholder) — zadziałają oba zapisy. Jeśli oczekuje prawdy/fałszu albo liczby (disabled, hidden, checked) — tylko nawiasy kwadratowe.
Odmiany, które przydadzą się od razu
| Zapis | Do czego |
|---|---|
[class.nazwa]="warunek" | dodaje jedną klasę CSS, gdy warunek jest prawdziwy, i usuwa, gdy fałszywy; pozostałe klasy elementu zostają |
[style.color]="wartosc" | ustawia jedną właściwość stylu (tekst, np. 'red') |
[style.width.rem]="18" | styl razem z jednostką: .px, .rem, .% |
[attr.aria-label]="opis" | atrybut HTML, który nie ma odpowiednika wśród właściwości elementu |
Klasy stałe wpisujesz zwykłym class="…", a te zależne od danych — osobnymi wiązaniami [class.x]. Oba zapisy mogą stać na jednym elemencie i nie przeszkadzają sobie.
Klamry kontra nawiasy kwadratowe
Dwa zapisy, ten sam wynik — przy tekście naprawdę są równoważne.
import { Component } from '@angular/core';
@Component({
selector: 'app-demo',
imports: [],
templateUrl: './demo.html'
})
export class Demo {
readonly opis = 'Turniej retro, piątek';
}
<div class="p-3">
<p title="{{ opis }}">Najedź myszą — dymek z klamer</p>
<p [title]="opis">Najedź myszą — dymek z nawiasów</p>
</div>
Najedź myszą — dymek z klamer → dymek: Turniej retro, piątek
Najedź myszą — dymek z nawiasów → dymek: Turniej retro, piątek
Oba akapity pokazują ten sam dymek. Pierwsza forma bywa wygodniejsza, gdy chcesz doklejać coś wokół wartości: title="Wydarzenie: {{ opis }}".
Zatrzymaj się
Pierwszy zapis zawsze tworzy tekst. Drugi przekazuje wartość. Przy napisie nie widać różnicy. Jak myślisz — co się stanie przy wartości false? Sprawdzisz w następnym przykładzie.
Tu klamry już nie wystarczą
Właściwość disabled nie interesuje się tekstem, tylko wartością logiczną.
import { Component } from '@angular/core';
@Component({
selector: 'app-demo',
imports: [],
templateUrl: './demo.html'
})
export class Demo {
readonly zablokowany = false;
}
<div class="p-3">
<button class="btn btn-primary me-2" [disabled]="zablokowany">Poprawnie</button>
<button class="btn btn-primary" disabled="{{ zablokowany }}">Źle</button>
</div>
[ Poprawnie ] — aktywny
[ Źle ] — zablokowany (wyblakły, nie da się kliknąć)
Drugi przycisk jest zablokowany, choć pole ma wartość false. Klamry zamieniły ją na tekst "false", a każdy niepusty tekst w disabled znaczy „zablokuj”.
Zatrzymaj się
Zmień pole na true i sprawdź oba przyciski. Który z nich w ogóle reaguje na zmianę wartości pola?
Wartość policzona w locie
W nawiasach kwadratowych może stać wyrażenie — tak samo jak w klamrach. Jedno pole steruje tu dwiema właściwościami.
import { Component } from '@angular/core';
@Component({
selector: 'app-demo',
imports: [],
templateUrl: './demo.html'
})
export class Demo {
readonly wolneMiejsca = 0;
readonly nazwa = 'Noc robotów';
}
<div class="card p-3 m-3" style="width: 22rem">
<h5>{{ nazwa }}</h5>
<p>Wolne miejsca: {{ wolneMiejsca }}</p>
<button class="btn btn-primary"
[disabled]="wolneMiejsca === 0"
[title]="wolneMiejsca === 0 ? 'Brak miejsc' : 'Kliknij, aby się zapisać'">
Zapisz się
</button>
</div>
Noc robotów
Wolne miejsca: 0
[ Zapisz się ] — zablokowany, dymek: Brak miejsc
Zmień wolneMiejsca na 3 — przycisk ożyje, a dymek zmieni treść na „Kliknij, aby się zapisać”. Długi atrybut można rozbić na kilka wierszy — Angular tego nie zauważy.
Zatrzymaj się
Warunek wolneMiejsca === 0 jest zapisany w szablonie dwa razy. Gdzie i jak przeniósłbyś go, żeby był zapisany raz?
Klasa CSS i styl sterowane danymi
Najczęstsze zastosowanie w tym kursie: włączanie i wyłączanie klasy Bootstrapa.
import { Component } from '@angular/core';
@Component({
selector: 'app-demo',
imports: [],
templateUrl: './demo.html'
})
export class Demo {
readonly ostatnieMiejsca = true;
readonly szerokosc = 18;
}
<div class="card p-3 m-3" [style.width.rem]="szerokosc">
<h5>Turniej retro</h5>
<span class="badge" [class.text-bg-danger]="ostatnieMiejsca" [class.text-bg-success]="!ostatnieMiejsca">
{{ ostatnieMiejsca ? 'Ostatnie miejsca' : 'Wolne miejsca' }}
</span>
</div>
┌──────────────────────┐
│ Turniej retro │
│ [Ostatnie miejsca] │ ← czerwona odznaka
└──────────────────────┘ szerokość 18rem
Zmień ostatnieMiejsca na false — odznaka zrobi się zielona, a napis się zmieni. Klasa badge jest wpisana na stałe, kolor sterują dwa wiązania z przeciwnymi warunkami (! to zaprzeczenie). Szerokość karty bierze się z pola: [style.width.rem] dokleja jednostkę za Ciebie.
Zatrzymaj się
Ustaw szerokosc na 30, a potem zmień wiązanie na [style.width]="szerokosc" (bez .rem). Co się stało z szerokością i dlaczego?
Zestawienie elementów
| Element | Znaczenie | Uwagi |
|---|---|---|
[title]="pole" | dymek z wartości pola | Dla tekstu działa też title="{{ pole }}". |
[disabled]="warunek" | blokada przycisku lub pola | Tylko nawiasy kwadratowe. |
[hidden]="warunek" | ukrycie elementu | Element zostaje w HTML, tylko go nie widać. |
[class.nazwa]="warunek" | włączenie jednej klasy | Pozostałe klasy zostają. |
[style.wlasciwosc]="tekst" | jeden styl | Np. [style.color]="'red'". |
[style.width.rem], .px, .% | styl z jednostką | Wartość jest liczbą. |
[attr.aria-label] | atrybut bez właściwości | Też [attr.colspan], [attr.aria-valuenow]. |
!warunek | zaprzeczenie | true ↔ false. |
text-bg-success, text-bg-danger | kolor odznaki | Bootstrap; z lekcji 03. |
Zanim utkniesz
| Zapis | Problem |
|---|---|
disabled="{{ x }}" | Przycisk zablokowany zawsze, także przy false — tekst "false" jest niepusty. |
[title]="Wydarzenie" | TS2339: Property 'Wydarzenie’ does not exist. W nawiasach kwadratowych to nazwa pola. Stały tekst: title="Wydarzenie" albo [title]="'Wydarzenie'". |
[src]="{{ adres }}" | NG5002: Got interpolation ({{}}) where expression was expected. Klamry i nawiasy kwadratowe wykluczają się — wybierz jedno. |
[class.badge]="true" | Działa, ale to klasa stała — wiązanie tylko zaciemnia kod. Stałe klasy wpisuj w zwykłym class="…". |
[style.width]="18" | Nic się nie stanie — brakuje jednostki. Napisz [style.width.rem]="18". |
[class.text-bg-danger, text-bg-success] | Jedno wiązanie = jedna klasa. Dwie klasy to dwa wiązania. |
[disabled]="wolne = 0" | Pojedyncze = to przypisanie — błąd NG5002. Porównanie to ===. |
Zadania
Dodaj do karty z przykładu 3 pole sala i pokaż je w dymku przycisku w postaci „Sala 12”. Zrób to dwoma sposobami — klamrami w title i wiązaniem [title] — a jeden z nich zostaw zakomentowany.
Kryterium sukcesu: najechanie na przycisk pokazuje „Sala 12” w obu wersjach.
Podpowiedź
W wiązaniu tekst skleisz plusem: [title]="'Sala ' + sala".
Dodaj pole zapisyOtwarte (true/false). Przycisk „Zapisz się” ma być zablokowany, gdy zapisy są zamknięte, a pod nim akapit „Zapisy trwają” albo „Zapisy zakończone”. Sprawdź obie wartości pola.
Kryterium sukcesu: przy false przycisk jest wyblakły i widać „Zapisy zakończone”; przy true — odwrotnie.
Podpowiedź
[disabled]="!zapisyOtwarte", a napis — operator ?: z lekcji 05.
Zrób pole wolneMiejsca i odznakę, która jest zielona przy więcej niż 5 miejscach, żółta przy 1–5 i czerwona przy zerze. Napis w odznace ma pokazywać liczbę miejsc.
Kryterium sukcesu: zmiana liczby w pliku .ts (np. 8, 3, 0) zmienia kolor odznaki bez zmian w szablonie.
Podpowiedź
Trzy osobne wiązania: [class.text-bg-success], [class.text-bg-warning], [class.text-bg-danger], każde z innym warunkiem. Środkowy warunek: wolneMiejsca >= 1 && wolneMiejsca <= 5.
Zrób kartę wydarzenia z polami polecane (true/false) i szerokosc (liczba). Gdy wydarzenie jest polecane, karta ma niebieską ramkę (border-primary) i cień (shadow), a nad tytułem pojawia się odznaka „Polecamy”. Szerokość karty w rem ma pochodzić z pola.
Kryterium sukcesu: przełączenie polecane zmienia ramkę, cień i widoczność odznaki; zmiana szerokosc zmienia szerokość karty.
Podpowiedź
Odznakę ukryjesz wiązaniem [hidden]="!polecane". Ramka i cień to dwa wiązania [class.…].
Zrób pasek postępu Bootstrapa pokazujący, jaka część miejsc jest zajęta. Szerokość paska ma wynikać z pól limit i zapisani, a w pasku ma być napis z procentem. Przy zajętości od 90% pasek ma być czerwony. Zadbaj o to, żeby przy limit = 0 nie powstało dzielenie przez zero.
Kryterium sukcesu: pasek zmienia długość i kolor po zmianie liczb w pliku .ts, a limit = 0 daje pusty pasek z napisem 0%.
Podpowiedź
Budowa paska: <div class="progress"><div class="progress-bar" [style.width.%]="procent()">{{ procent() }}%</div></div>. Metoda procent() zwraca 0, gdy limit jest zerem; czerwień — [class.bg-danger]. Zaokrąglenie: Math.round w metodzie.
Zrób kartę „Twoja ocena” z polem ocena (1–6). Cyfra oceny ma być duża, a jej kolor zależny od wartości: czerwony dla 1, pomarańczowy dla 2, czarny dla 3–4, zielony dla 5–6. Rozmiar czcionki ma rosnąć z oceną: 1 + ocena * 0.5 rem. Kolor wybierz w metodzie kolor(), która zwraca nazwę koloru CSS. Dodaj też [attr.aria-label] z pełnym opisem, np. „Ocena: 5”.
Kryterium sukcesu: dla ocen 1, 2, 4 i 6 cyfra ma inny kolor i rozmiar, a w narzędziach przeglądarki (F12 → Elements) widać atrybut aria-label.
Podpowiedź
[style.color]="kolor()" i [style.font-size.rem]="1 + ocena * 0.5". W metodzie kilka if z return 'red', return 'orange' itd.
Co trzeba zapamiętać
{{ }}daje tekst,[ ]przekazuje wartość.- Przy właściwościach logicznych, takich jak
disabled, klamry zawsze blokują — tylko[disabled]działa poprawnie. - W nawiasach kwadratowych stoi wyrażenie: nazwa pola, obliczenie, metoda albo
?:; stały tekst wymaga apostrofów. [class.nazwa]włącza jedną klasę, nie ruszając pozostałych.[style.x.jednostka]ustawia jeden styl z jednostką.[attr.x]ustawia atrybut, którego nie ma wśród właściwości.[ ]i{{ }}w jednym atrybucie dają błąd NG5002.
Sprawdź się: masz pole ukryty = false. Napisz dwa warianty blokowania przycisku tym polem i powiedz, który z nich zadziała zgodnie z oczekiwaniem.
Dokumentacja: angular.dev — wiązanie właściwości i atrybutów.