Opiniotwórcza struktura Angulara jest atutem, gdy wiesz, jak z nią pracować. Te przepisy dają ci prompty, które generują kod Angular zgodny z konwencjami frameworka — komponenty standalone, sygnały do reaktywności, prawidłowe wstrzykiwanie zależności i wzorce RxJS, które nie powodują wycieków subskrypcji.
Przepisy na komponenty standalone z sygnałami i nową składnią kontroli przepływu
Wzorce serwisów i wstrzykiwania zależności z właściwym typowaniem
Przepisy RxJS na złożone przepływy asynchroniczne bez wycieków subskrypcji
Prompty testowe dla TestBed, component harnesses i mockowania HTTP
Scenariusz: Potrzebujesz komponentu karty produktu wykorzystującego najnowsze komponenty standalone i API sygnałów Angular.
Wskazówka
Utwórz komponent standalone Angular w src/app/components/product-card/product-card.component.ts. Użyj funkcji input() dla typowanych inputów: product (wymagany, typ Product), showActions (opcjonalny, domyślnie true). Użyj output() dla typowanych outputów: addToCart emituje Product, viewDetails emituje string (ID produktu). Użyj computed() dla stanu pochodnego: formattedPrice (formatuje product.price z walutą), isOnSale (sprawdza czy discountPercentage > 0), discountedPrice (obliczony z ceny i rabatu). Użyj nowej składni kontroli przepływu @if, @for, @switch w szablonie zamiast *ngIf i *ngFor. Styluj z Tailwind CSS. Dołącz stan ładowania szkieletu. Napisz plik spec używając TestBed z provideExperimentalZonelessChangeDetection() do testowania opartego na sygnałach. Pokryj wszystkie kombinacje inputów i emisje outputów.
Oczekiwany wynik: Plik TypeScript komponentu, szablon inline z nową kontrolą przepływu i plik spec.
Scenariusz: Twój serwis uwierzytelniania używa BehaviorSubjects i manualnych subskrypcji wszędzie. Zmodernizuj go z sygnałami, zachowując RxJS dla HTTP.
Wskazówka
Zrefaktoryzuj src/app/services/auth.service.ts, aby używać sygnałów dla stanu i RxJS tylko dla operacji asynchronicznych. (1) Zamień BehaviorSubject<User | null> na signal<User | null>(null). (2) Utwórz computed signals: isAuthenticated = computed(() => this.currentUser() !== null), userRole = computed(() => this.currentUser()?.role ?? 'guest'). (3) Zachowaj wywołania HttpClient jako Observable, ale konwertuj wyniki na aktualizacje sygnałów: login zwraca Observable<void>, wewnętrznie wywołuje API i ustawia sygnał użytkownika. (4) Użyj effect() do persystencji stanu uwierzytelniania w localStorage. (5) Dodaj toObservable(this.currentUser) dla kompatybilności z Observable podczas migracji. (6) Zaimplementuj odświeżanie tokenu: HttpInterceptor przechwytujący odpowiedzi 401, wywołujący refreshToken(), ponawiający oryginalne żądanie i wylogowujący, jeśli odświeżanie się nie powiedzie. (7) Zarejestruj jako providedIn: 'root'. Napisz testy weryfikujące, że login ustawia sygnał, logout go czyści, a interceptor obsługuje odświeżanie tokenu.
Oczekiwany wynik: Zrefaktoryzowany serwis, interceptor HTTP i kompleksowy plik testowy.
Scenariusz: Twój formularz rejestracji wymaga walidacji międzypolowej, walidacji asynchronicznej i pól warunkowych.
Wskazówka
Utwórz formularz rejestracji używając typowanych formularzy reaktywnych Angular. (1) Zdefiniuj formularz z NonNullableFormBuilder: email (wymagany, format email, asynchroniczny walidator unikalności z debounce 300ms), password (wymagany, minLength 8, wzorzec wymagający wielka litera+cyfra+znak specjalny), confirmPassword (wymagany, musi pasować do hasła przez walidator międzypolowy), accountType (‘personal’ | ‘business’), companyName (wymagany tylko gdy accountType to ‘business’). (2) Utwórz niestandardowe walidatory: matchField(fieldName) dla potwierdzenia hasła, asyncEmailUnique() zwracający AsyncValidatorFn. (3) Utwórz komponent wielokrotnego użytku ValidationMessage przyjmujący FormControl i mapę błąd-komunikat. (4) Dynamicznie pokazuj/ukrywaj companyName na podstawie accountType. (5) Wyłącz przycisk submit, dopóki formularz nie jest prawidłowy i nie czeka. Testuj walidację formularza włącznie z asynchronicznym sprawdzaniem email z fakeAsync.
Oczekiwany wynik: Komponent formularza, niestandardowe walidatory, komponent ValidationMessage i testy z fakeAsync.
Scenariusz: Twoja sekcja administratora ma 8 stron i ładuje wszystko z góry. Załaduj ją leniwie jako trasy standalone.
Wskazówka
Zrefaktoryzuj sekcję administratora, aby używała leniwie ładowanych tras standalone. (1) Utwórz src/app/admin/admin.routes.ts z trasami dla pulpitu, użytkowników, ustawień, raportów. Każda ładuje się z loadComponent: () => import('./pages/admin-dashboard.component'). (2) W głównych trasach, załaduj leniwie admin: { path: 'admin', loadChildren: () => import('./admin/admin.routes').then(m => m.ADMIN_ROUTES), canActivate: [authGuard], data: { roles: ['admin'] } }. (3) Utwórz funkcyjną authGuard używającą inject(AuthService) do sprawdzania uwierzytelniania i roli. (4) Utwórz AdminLayoutComponent z nawigacją w pasku bocznym, okruszkami nawigacyjnymi i router-outlet. (5) Utwórz provideAdminServices() dla serwisów administratora na poziomie trasy, niszczonych przy nawigacji. (6) Zweryfikuj, że fragment admin ładuje się tylko przy nawigacji do /admin. Testuj, że guard przekierowuje nieautoryzowanych użytkowników.
Oczekiwany wynik: Trasy administratora, komponent layoutu, funkcyjny guard, dostawcy na poziomie trasy i testy guarda.
Scenariusz: Twoja aplikacja potrzebuje scentralizowanego stanu, ale klasyczny NgRx actions/reducers/effects wydaje się ciężki. Użyj SignalStore.
Wskazówka
Zaimplementuj zarządzanie stanem używając @ngrx/signals SignalStore dla produktów. (1) Utwórz src/app/stores/products.store.ts używając signalStore() ze stanem: tablica products, selectedProductId, obiekt filters, boolean loading, string error. (2) Dodaj withComputed(): filteredProducts (stosuje wszystkie filtry), selectedProduct (znajduje po ID), totalCount, hasActiveFilters. (3) Dodaj withMethods(): loadProducts() (wywołuje API), selectProduct(id), setFilter(key, value), clearFilters(), addProduct, updateProduct, deleteProduct z optymistycznym usuwaniem. (4) Dodaj withHooks() dla onInit do ładowania produktów. (5) Dodaj rxMethod dla debounce wyszukiwania. (6) Dostarcz na poziomie trasy funkcji. Napisz testy weryfikujące, że wartości computed się aktualizują, metody wywołują API, a optymistyczne aktualizacje wycofują się przy błędzie.
Oczekiwany wynik: Plik SignalStore, serwis API i kompleksowe testy store.
Scenariusz: Twoja aplikacja potrzebuje tokenów uwierzytelniania, logowania, obsługi błędów i cache’owania jako interceptorów HTTP.
Wskazówka
Utwórz funkcyjne interceptory HTTP w src/app/interceptors/. (1) authInterceptor — dodaje token Bearer, pomija publiczne endpointy. (2) loggingInterceptor — loguje metodę, URL, czas; w trybie dev loguje ciała. (3) errorInterceptor — przechwytuje 401 (refresh + retry), 403 (przekierowanie), 429 (toast z odliczaniem), 500+ (generyczny toast). Wysyła do śledzenia błędów. (4) cachingInterceptor — cache’uje odpowiedzi GET w Map z TTL, pomija jeśli Cache-Control: no-cache. (5) Zarejestruj w app.config.ts z provideHttpClient(withInterceptors([...])) w prawidłowej kolejności (auth pierwszy, caching, logging, error ostatni). Testuj każdy interceptor w izolacji z HttpTestingController.
Oczekiwany wynik: Cztery pliki interceptorów, rejestracja w app config i indywidualne pliki testowe.
Scenariusz: Twoja aplikacja renderuje formularze ze schematu JSON z backendu. Potrzebujesz dynamicznego generatora formularzy.
Wskazówka
Zbuduj system dynamicznych formularzy. (1) Zdefiniuj interfejs FormFieldConfig: type (text, email, number, select, checkbox, radio, textarea, date), key, label, validators (required, minLength, maxLength, pattern, custom), options (dla select/radio), defaultValue, dependsOn (warunkowa widoczność). (2) Utwórz DynamicFormComponent przyjmujący FormFieldConfig[], budujący FormGroup dynamicznie, renderujący każde pole używając @switch na type, obsługujący warunkową widoczność. (3) Utwórz indywidualne komponenty pól dla każdego typu. (4) Formularz emituje (formSubmit) z typowaną wartością i (formChange) przy każdej zmianie. (5) Utwórz FormSchemaService pobierający schemat z API z cache’owaniem. (6) Testuj, że schemat JSON produkuje prawidłowe pola, walidacja działa, a pola warunkowe przełączają się poprawnie.
Oczekiwany wynik: Interfejs konfiguracji formularza, komponent dynamicznego formularza, komponenty pól, serwis schematu i testy.
Scenariusz: Strony pokazują błysk pustej zawartości przed załadowaniem danych. Potrzebujesz resolwerów, które ładują dane przed renderowaniem.
Wskazówka
Zaimplementuj funkcyjne resolwery tras. (1) Utwórz productResolver jako ResolveFn<Product> czytający id z params, wywołujący ProductService.getById(id), przekierowujący do /products z toast przy not-found. (2) Utwórz userProfileResolver resolwujący profil uwierzytelnionego użytkownika. (3) W konfiguracji trasy, dodaj resolve: { product: productResolver }. (4) W komponentach, dostęp do resolwowanych danych przez input() z kluczem resolve. (5) Dodaj globalny RouteLoadingComponent pokazujący się podczas wykonywania resolvera używając Router.events. (6) Dodaj obsługę błędów: jeśli resolver rzuci wyjątek, przekieruj do strony błędu. Testuj, że resolvery zwracają prawidłowe dane i obsługują błędy.
Oczekiwany wynik: Dwie funkcje resolwerów, komponent ładowania, obsługa błędów i testy resolwerów.
Scenariusz: Twoje szablony powtarzają tę samą manipulację DOM. Potrzebujesz niestandardowych dyrektyw dla typowych zachowań.
Wskazówka
Utwórz dyrektywy w src/app/directives/. (1) ClickOutsideDirective — emituje przy kliknięciach poza elementem hosta. (2) IntersectionDirective — emituje, gdy element wchodzi/wychodzi z viewport. (3) PermissionDirective — dyrektywa strukturalna jak *appPermission="'admin'" pokazująca/ukrywająca na podstawie roli. (4) AutofocusDirective — fokusuje po konfigurowalnym opóźnieniu, obsługuje SSR. (5) LongPressDirective — emituje po przytrzymaniu 500ms, anuluje przy opuszczeniu. (6) TooltipDirective — pokazuje podpowiedź przy hover z konfigurowalną pozycją i opóźnieniem. Każda dyrektywa powinna być standalone i importowalna indywidualnie. Napisz testy dla każdej używając testowania component harness.
Oczekiwany wynik: Sześć plików dyrektyw i sześć plików testowych.
Scenariusz: Twoja aplikacja Angular potrzebuje SSR dla SEO i wydajności początkowego ładowania z pełną hydracją.
Wskazówka
Skonfiguruj Angular SSR z hydracją. (1) Dodaj @angular/ssr. (2) Skonfiguruj server.ts z Express i proxy API. (3) Włącz hydrację w app.config z provideClientHydration(withEventReplay()). (4) Utwórz PlatformService owijający isPlatformBrowser/isPlatformServer. (5) Audituj komponenty pod kątem kompatybilności SSR: zamień window/document/localStorage na warianty zabezpieczone platformowo. Utwórz BrowserStorageService wracający do Map w pamięci na serwerze. (6) Dodaj TransferState dla żądań HTTP, aby zapobiec duplikatom zapytań. (7) Dodaj meta tagi używając serwisów Meta i Title w resolwerach. Testuj wyjście SSR.
Oczekiwany wynik: Konfiguracja SSR, serwis platformy, serwis storage, integracja transfer state i testy.
Scenariusz: Twoja monolityczna aplikacja Angular musi być podzielona na niezależnie deployowalne mikro-frontendy.
Wskazówka
Skonfiguruj Module Federation dla mikro-frontendów Angular. (1) Skonfiguruj shell z @angular-architects/module-federation: zdefiniuj remote w konfiguracji webpack. (2) Skonfiguruj remote (produkty): eksponuj ProductsModule. (3) Utwórz narzędzie loadRemoteModule z obsługą błędów i retry. (4) W routingu shella, leniwie załaduj remote. (5) Utwórz współdzieloną bibliotekę dla typów, serwisu komunikacji i współdzielonego UI. (6) Skonfiguruj sharedMappings dla Angular core, aby zapewnić pojedynczą instancję. (7) Dodaj UI zastępcze, gdy remote zawiedzie. Testuj, że shell ładuje remote, a współdzielony stan się synchronizuje.
Oczekiwany wynik: Konfiguracje hosta i remote, narzędzie loadera, współdzielona biblioteka, komponent zastępczy i testy.
Scenariusz: Twoje szablony mają wszędzie inline manipulację stringami. Wyekstrahuj je do wielokrotnego użytku pipes.
Wskazówka
Utwórz standalone pipes w src/app/pipes/. (1) RelativeTimePipe — transformuje Date do “2 minuty temu”, itp. (2) TruncatePipe — skraca do długości z opcjonalną granicą słowa. (3) FileSizePipe — bajty do KB/MB/GB z konfigurowalnymi miejscami dziesiętnymi. (4) HighlightPipe — owija pasujące podciągi w tagi mark używając DomSanitizer. (5) PluralizePipe — liczba z formami pojedynczą/mnogą. (6) InitialsPipe — ekstrahuje inicjały z nazwiska. (7) CurrencyFormatPipe — formatowanie świadome locale z Intl.NumberFormat. Wszystkie standalone i tree-shakeable. Testuj każdy z przypadkami brzegowymi (null, undefined, pusty string, zero, ujemne).
Oczekiwany wynik: Siedem plików pipe z testami przypadków brzegowych dla każdego.
Wzorce React do porównania podejść między frameworkami
Wzorce API do budowania backendów obsługujących twoją aplikację Angular
Przepisy CI/CD dla potoków budowania i wdrażania Angular