Samouczek Angular
Samodzielne komponenty Angulara 20 — wejścia sygnałowe, wykrywanie zmian OnPush, szablony wbudowane, zero NgModules, zero CSS.
Co zbudujesz
Mały formularz kontaktowy — pole z etykietą, przycisk wysyłania, wiązanie dwukierunkowe, prawdziwe działanie klawiatury i czytnika ekranu — najpierw surowy, potem ostylowany, w około 10 minut.
Zanim zaczniesz
Potrzebujesz gita, Node 22 lub nowszego i pnpm 10 lub nowszego. Dowolny projekt Angular 20 się nadaje — Angular CLI lub Analog.
Krok 1 — Pobierz kod
git clone https://github.com/LilyDesignSystem/lily-design-system
cd lily-design-system/lily-design-system-angular-headless
pnpm install
pnpm test # vitest + TestBed, all 571 components
pnpm run build-storybook
Komponenty to płaskie pliki w components/ —
Button.ts, TextInput.ts — każdy jest samodzielnym komponentem z szablonem wbudowanym, plus jego spec i story. Barrel
index.ts ponownie eksportuje każdy komponent; pnpm build
tworzy pakiet w formacie Angular Package Format przez ng-packagr.
Krok 2 — Twój pierwszy komponent
import { Component } from "@angular/core";
import { Button } from "@lilydesignsystem/angular-headless";
@Component({
selector: "app-save-bar",
standalone: true,
imports: [Button],
template: `<lily-button (click)="save()">Save</lily-button>`,
})
export class SaveBar {
save() { console.log("saved"); }
}
Każdy komponent deklaruje selektor lily-* i renderuje element semantyczny z jego haczykiem klasy —
<button class="button" type="button"> tutaj. Wejścia są oparte na sygnałach: label() dla elementu aria-label
do nadpisywania, className() do dopisywania klas.
Krok 3 — Złóż mały formularz
import { Component, signal } from "@angular/core";
import { Form, Field, TextInput, Button } from "@lilydesignsystem/angular-headless";
@Component({
selector: "app-contact-form",
standalone: true,
imports: [Form, Field, TextInput, Button],
template: `
<lily-form label="Contact form" (submit)="handleSubmit($event)">
<lily-field label="Full name" [required]="true">
<lily-text-input id="name" label="Full name" [(value)]="name" />
</lily-field>
<lily-button type="submit">Save</lily-button>
</lily-form>
`,
})
export class ContactForm {
name = signal("");
}
lily-field przyjmuje widoczną etykietę jako własne wejście
label zamiast zagnieżdżonego komponentu etykiety — przekaż ją raz, na polu, a pasujący lily-text-inputz
label dostarcza dostępną nazwę. Komponenty podobne do pól udostępniają wartości model() więc banana-box [(value)]
wiązanie dwukierunkowe działa od razu — zgodne z zoneless w całości.
Zweryfikowano 2026-08-29: wcześniejszy szkic zagnieżdżał osobny komponent Label wewnątrz lily-field, co renderowało dwie etykiety (jedną pustą), ponieważ
lily-field już renderuje własną z wejścia
label — wykryto, sprawdzając kod źródłowy komponentu.
Złożona trasa formularza kontaktowego
to ten sam wzorzec, sprawdzony prawdziwymi testami e2e i axe.
Sprawdź swoją pracę: uruchom aplikację, a formularz wygląda surowo — dokładnie tak, jak zamierzono: stylowanie jest jeszcze przed nami. Naciśnij Tab: fokus przechodzi przez każdą kontrolkę, etykieta jest odczytywana, a wysyłanie działa. Zachowanie jest gotowe; wygląd należy do ciebie i jest następnym krokiem.
Krok 4 — Ostyluj go
Ostyluj haczyki klas — .button, .text-input,
.label, .field — w styles.csslub dodaj gotowy motyw do tablicy styles w
angular.json / podlinkuj go w index.html:
<link rel="stylesheet" href="/themes/adobe-spectrum.css" />
Krok 5 — Zobacz, jak to wszystko działa
Aplikacja przykładowa Angular + Analog pokazuje cały katalog z routingiem opartym na plikach i Vite:
git clone https://github.com/LilyDesignSystem/lily-design-system
cd lily-design-system/lily-design-system-angular-examples
pnpm install && pnpm run dev
Następne kroki
- Motywy — przełączanie w czasie działania pomocnikiem theme-picker dla Angulara.
- Pomocniki preferencji — język i rozmiar tekstu.
- Katalog komponentów — właściwości, ARIA i klawiatura dla każdego komponentu.
Pytania po drodze? Strona pomocy jest pełna odpowiedzi — a jeśli jakiś krok mógłby być jaśniejszy, daj nam znać a chętnie ulepszymy samouczek.