Trudny / egzaminacyjny · lekcja 11

Angular III — formularze reaktywne, walidacja i routing SPA

Ostatni element układanki Angulara: prawdziwe formularze z walidacją (nie tylko proste [(ngModel)] z lekcji 10) oraz nawigacja między "stronami" aplikacji jednostronicowej (SPA) — bez przeładowania całej strony w przeglądarce. Po tej lekcji będziesz mieć kompl...

Zacznij tutaj

Po co się tego uczymy?

Ostatni element układanki Angulara: prawdziwe formularze z walidacją (nie tylko proste [(ngModel)] z lekcji 10) oraz nawigacja między "stronami" aplikacji jednostronicowej (SPA) — bez przeładowania całej strony w przeglądarce. Po tej lekcji będziesz mieć komplet wiedzy potrzebnej do zbudowania w pełni funkcjonalnej aplikacji Angular, gotowej do połączenia z backendem ASP.NET Core w kolejnych lekcjach.

Wprowadzenie

Teoria

Dwa podejścia do formularzy w Angularze. [(ngModel)] z lekcji 10 (tzw. "formularze szablonowe") sprawdza się w prostych przypadkach, ale profesjonalne, większe formularze buduje się przez Reactive Forms — podejście, gdzie CAŁA struktura formularza (pola, reguły walidacji) jest zdefiniowana w KODZIE TypeScript, a szablon HTML tylko się do niej "podłącza". To podejście jest bardziej przewidywalne i łatwiejsze do testowania w większych aplikacjach.

Budowa formularza reaktywnego. Klasa FormGroup reprezentuje CAŁY formularz, a FormControl — POJEDYNCZE pole, z opcjonalną wartością początkową i tablicą walidatorów:

import { FormGroup, FormControl, Validators } from '@angular/forms';

formularz = new FormGroup({
  nazwa: new FormControl('', [Validators.required, Validators.minLength(2)]),
  cena: new FormControl(0, [Validators.required, Validators.min(0.01)])
});

W szablonie HTML formularz "podłącza się" atrybutem [formGroup]="formularz" na znaczniku <form>, a każde pole atrybutem formControlName="nazwa". Sprawdzenie poprawności: formularz.valid (cały formularz), formularz.get('nazwa')?.invalid (pojedyncze pole).

Wbudowane walidatory Angulara (klasa Validators) — bezpośredni odpowiednik Data Annotations z lekcji o ASP.NET Core:

Walidator AngularOdpowiednik w C# (Data Annotations)
Validators.required[Required]
Validators.minLength(n) / maxLength(n)[StringLength]
Validators.min(n) / max(n)[Range]
Validators.email[EmailAddress]
Validators.pattern(regex)własny atrybut walidacyjny z wyrażeniem regularnym

Walidacja HTML5 (natywna przeglądarki). Niezależnie od Angulara, sam HTML5 oferuje podstawową walidację przez atrybuty znaczników: required, type="email", min/max, pattern — przeglądarka SAMA blokuje wysłanie formularza i pokazuje wbudowany komunikat, jeśli te warunki nie są spełnione. To dobre uzupełnienie (pierwsza linia obrony), ale walidacja Angulara (Reactive Forms) daje PEŁNĄ kontrolę nad wyglądem komunikatów i logiką.

Routing — nawigacja SPA. Plik app.routes.ts definiuje mapowanie adresów URL na komponenty: { path: 'produkty', component: ProduktyComponent }. Znacznik <router-outlet></router-outlet> w głównym komponencie aplikacji wskazuje MIEJSCE, gdzie ma się pojawić aktualnie aktywny komponent-strona — dokładnie ten sam mechanizm co @RenderBody() w Blazor albo @Body w layoucie. Nawigacja z poziomu szablonu: <a routerLink="/produkty">Produkty</a>; z poziomu kodu: wstrzyknięty Router i wywołanie this.router.navigate(['/produkty']).

Parametry w adresie. Trasa { path: 'produkty/:id', component: SzczegolyProduktuComponent } pozwala odebrać wartość przez wstrzyknięty ActivatedRoute: this.route.snapshot.paramMap.get('id') — koncepcyjnie identyczne do parametrów adresu w Razor Pages/Blazor z wcześniejszych lekcji tego kursu.

Zobacz zależności

Schemat

Reactive Forms:
FormGroup (cały formularz)
  ├── FormControl "nazwa"  [Validators.required, minLength(2)]
  └── FormControl "cena"   [Validators.required, min(0.01)]
        │
        │  formularz.valid?
   niepoprawny ──┴── poprawny
   │                     │
   ▼                     ▼
pokaż błędy         onSubmit() → wyślij dane dalej


Routing SPA:
app.routes.ts:
  { path: "",          component: StronaGlownaComponent }
  { path: "produkty",  component: ProduktyComponent }
  { path: "produkty/:id", component: SzczegolyProduktuComponent }

app.component.html:
  <nav>
    <a routerLink="/">Start</a>
    <a routerLink="/produkty">Produkty</a>
  </nav>
  <router-outlet></router-outlet>   ← TU wstawiany aktywny komponent

Kliknięcie linku "/produkty" → Router podmienia zawartość <router-outlet>
BEZ przeładowania całej strony (charakterystyczne dla SPA)

Połącz z praktyką

Przykład z życia

Formularz rejestracji nowego produktu w panelu sprzedawcy pokazuje na bieżąco, czy nazwa jest wystarczająco długa i czy cena jest liczbą dodatnią, dokładnie tak jak w Data Annotations z ASP.NET Core — a menu z linkami "Strona główna / Produkty / Kontakt" przełącza widoczną treść błyskawicznie, bez przeładowania całej strony, bo to jedna aplikacja Angular obsługująca kilka "widoków".