Angular — pytania rekrutacyjne z zadaniem o formularzu i stanie

Angular: pytania rekrutacyjne i formularz wyszukiwania. Sprawdź PENDING, async validators, signals, switchMap i sprzątanie w 22 kontrolach Angular22.2.2.

Author: PracHub

Published: 10/11/2026

Angular — pytania rekrutacyjne z zadaniem o formularzu i stanie

October 11, 2026

Quick Overview

Polskie ćwiczenie Angular22.2.2 i RxJS7.8.2: kontrolowana walidacja kodu katalogu, własność aktualnego wyszukiwania, errors oraz teardown po destroy.

Software EngineerFree

Formularz może przyjąć poprawny kod, a mimo to pokazać wynik poprzedniego wyszukiwania. Inny błąd jest mniej widoczny: przycisk dopuszcza wysłanie, kiedy walidator nadal czeka na odpowiedź. Na rozmowie rekrutacyjnej z Angulara warto umieć odróżnić te dwie sytuacje i pokazać, kto odpowiada za każdą subskrypcję.

Prześledź autorskie wyszukiwanie po kodzie katalogu. Ćwiczenie łączy Reactive Forms, asynchroniczną walidację, computed, switchMap i niszczenie komponentu. Następnie przejdź do Debug an Angular UI from User Reports, aby przećwiczyć diagnozę na podstawie objawów zamiast listy definicji.

Granica dowodów: fakty o API przypisujemy oficjalnej dokumentacji. Kontrakt katalogu, kontrolowane odpowiedzi i testy są autorskie. Laboratorium uruchomiono w Chrome z Angular22.2.2 i RxJS7.8.2; przeszły 22 kontrole modelu, renderowanego DOM i sprzątania. Nie wykonano prawdziwych żądań HTTP. Nie korzystamy z relacji kandydatów i nie twierdzimy, że konkretna firma zadaje te pytania.

Walidacja bieżącego kodu katalogu i zamknięcie poprzedniej subskrypcji

Najpierw podaj wersję i zakres zadania

W tym tekście „Angular” oznacza współczesny framework, a nie AngularJS z $scope i cyklem digest. Używamy istniejącego API Reactive Forms oraz signals do odczytu i wyliczania stanu. Nie jest to implementacja osobnego API Signal Forms. Podobne nazwy nie oznaczają identycznego modelu ani możliwości przeniesienia przykładu bez zmian między wersjami.

Laboratorium to samodzielny komponent uruchomiony przez bootstrapApplication, z szablonem kompilowanym JIT i modułem ReactiveFormsModule. Nie ładuje Zone.js. Oficjalny przewodnik zoneless opisuje mechanizmy powiadamiania Angulara o aktualizacjach; tutaj sprawdziliśmy rzeczywisty odczyt statusu i wyniku w DOM. Nie zmierzyliśmy szybkości renderowania ani rozmiaru produkcyjnego pakietu. Bundle deweloperski nie jest benchmarkiem.

Dobra odpowiedź otwierająca brzmi: „Pokażę zachowanie w wersji22.2.2. Oddzielę poprawność pola od własności ostatniego wyszukiwania i wskażę, które subskrypcje kończy komponent”. Dzięki temu rozmówca może sprawdzić, czy kod spełnia podany kontrakt w zadeklarowanej wersji.

Kontrakt: poprawny kod nie jest jeszcze wynikiem wyszukiwania

Pole przyjmuje dokładnie trzy wielkie litery ASCII, na przykład XYZ. To umowny kod katalogu, nie uniwersalna reguła dla nazw produktów. Najpierw sprawdzamy format lokalnie. Dopiero po jego zaakceptowaniu kontrolowana usługa odpowiada, czy kod istnieje. Odpowiedź false oznacza unknownCode; awaria sprawdzenia oznacza validationUnavailable.

Wyszukiwanie uruchamia osobny przycisk i osobny strumień. Można wysłać tylko pole o statusie VALID. Wysłanie następnego poprawnego kodu zastępuje poprzednie wyszukiwanie. Samo rozpoczęcie edycji pola nie anuluje tutaj ostatniego zatwierdzonego wyszukiwania: wynik zachowuje etykietę własnego kodu. To świadomy, ograniczony kontrakt ćwiczenia, a nie ukryte założenie automatycznego typeahead.

ZdarzenieStan polaCzy można wysłać?Oczekiwane zachowanie
Puste pole lub ABINVALIDNieBrak wywołania walidacji zdalnej
Wpisanie ABCPENDINGNieRozpoczęta walidacja kodu
Zmiana ABC na ABDPENDINGNiePoprzednia walidacja odsubskrybowana
Odpowiedź false dla ABDINVALIDNieBłąd unknownCode
Odpowiedź true dla XYZVALIDTakMożna uruchomić wyszukiwanie
Nowe wyszukiwanie LMNVALIDTakPoprzednie wyszukiwanie przestaje dostarczać wyniki
Zniszczenie komponentuPoza cyklem widokuNie dotyczyAktywna subskrypcja wyszukiwania zamknięta

Zapytaj rozmówcę, czy wynik ma znikać natychmiast po każdej zmianie pola. Jeżeli tak, trzeba zmienić kontrakt i połączyć edycję z anulowaniem lub czyszczeniem wyników. Nie przedstawiaj obecnego rozwiązania jako implementacji tego dodatkowego wymagania.

Dlaczego samo !invalid jest niebezpieczne?

Oficjalna dokumentacja walidacji Angulara rozróżnia walidację synchroniczną i asynchroniczną. Asynchroniczna jest uruchamiana po przejściu sprawdzeń synchronicznych; kontrolka może mieć wtedy status PENDING. Walidator zwraca Promise lub skończony Observable. Dokumentacja opisuje też używanie stanów dirty i touched do sterowania komunikatami.

W naszym kontrakcie przycisk wymaga dokładnie VALID. Warunek !code.invalid nie wyraża tej samej decyzji: oczekiwanie nie jest potwierdzeniem poprawności. W laboratorium po ABC kontrolka pozostawała pending, a wyliczone zezwolenie na wyszukiwanie wynosiło false. To kontrola konkretnej ścieżki, nie dowód obsługi każdego możliwego stanu formularza.

readonly code = new FormControl('', {
  nonNullable: true,
  validators: [Validators.required, Validators.pattern(/^[A-Z]{3}$/)],
  asyncValidators: [control =>
    this.validation.request(control.value).pipe(
      take(1),
      map(ok => ok ? null : { unknownCode: true }),
      catchError(() => of({ validationUnavailable: true }))
    )
  ]
});

W tym fragmencie validation jest kontrolowaną usługą testową. Nie ma serwera ani ukrytego zapytania sieciowego. take(1) pozwala zakończyć ścieżkę po pierwszej odpowiedzi. Jeżeli źródło nigdy nie odpowie ani się nie zakończy, pole może nadal czekać; kod nie implementuje timeoutu. Puste zakończenie źródła również wymaga osobnej polityki i testu.

Przy awarii nie zwracamy null, ponieważ kontrakt wymaga potwierdzonego istnienia kodu. To nasza decyzja biznesowa, nie reguła nakazująca wszystkim aplikacjom blokowanie formularza przy każdym błędzie sieci.

Signals: wylicz uprawnienie z jednego statusu

Według oficjalnego przewodnika signals computed wyprowadza wartość z odczytywanych signals. Dokumentacja interoperacyjności RxJS wyjaśnia, że toSignal subskrybuje Observable i udostępnia jego bieżącą wartość; trzeba również wybrać wartość początkową.

readonly status = toSignal(this.code.statusChanges, {
  initialValue: this.code.status
});
readonly canSearch = computed(() => this.status() === 'VALID');

Nie tworzymy osobnych zapisywalnych flag valid, pending, canSubmit aktualizowanych w kilku callbackach. Pominięcie aktualizacji mogłoby zostawić aktywny przycisk obok statusu PENDING. W tym przykładzie zezwolenie wynika bezpośrednio ze statusu; nie potrzebuje efektu kopiującego dane do następnego pola.

Uważaj jednak na skrót computed(() => this.code.valid). Zwykłe pole klasy nie staje się signal tylko dlatego, że odczytasz je wewnątrz computed. Nasza zależność to jawny status(), zasilany przez statusChanges. W testach sprawdziliśmy zarówno wartość wyliczoną, jak i tekst VALID wyrenderowany w elemencie strony.

To uzasadnienie spójności stanu. Nie twierdzimy, że dwa wiersze kodu automatycznie rozwiązują problemy wydajnościowe całej aplikacji, głęboką mutację obiektów czy dostępność komunikatów dla czytnika ekranu.

Dwa rodzaje anulowania, dwie osobne obserwacje

W scenariuszu walidacji wpisaliśmy ABC, a przed odpowiedzią zmieniliśmy pole na ABD. Kontrola old_validation_unsubscribed potwierdziła zamknięcie subskrypcji ABC. Późniejsze ręczne dostarczenie odpowiedzi do starego subskrybenta nie zmieniło oczekiwania na ABD. Odpowiedź false dla aktualnego kodu ustawiła błąd unknownCode. Nie zastępuj tego wyniku hasłem „Angular zawsze anuluje wszystko”.

Wyszukiwanie jest inną operacją. Oficjalny opis RxJS switchMap w źródle wersji7.8.2 opisuje przełączanie na nowe wewnętrzne Observable. Nasze zastosowanie ma zachować tylko wyniki ostatniego wysłanego wyszukiwania:

this.submitted.pipe(
  switchMap(query => this.searches.request(query).pipe(
    map(items => ({ kind: 'ready' as const, query, items })),
    catchError(() => of({ kind: 'error' as const, query, items: [] })),
    startWith({ kind: 'loading' as const, query, items: [] })
  )),
  takeUntilDestroyed(this.destroyRef)
).subscribe(state => this.state.set(state));

submitted jest Subjectem kodów przekazanych przez metodę search, a state jest signal ze stanem idle/loading/ready/error i etykietą query. Metoda wysyłająca sprawdza code.valid; wyłączenie przycisku nie jest jedynym miejscem kontroli. Usługa zwraca Observable listy identyfikatorów katalogowych.

Anulowanie subskrypcji oznacza, że ten konsument przestaje odbierać dane i uruchamia teardown źródła. Nasz mock zapisuje ten fakt. Nie wysłaliśmy HTTP, więc nie zmierzyliśmy przerwania transportu, pracy backendu ani rozliczenia zewnętrznego żądania. Taką różnicę trzeba umieć nazwać także wtedy, gdy w produkcji używasz HttpClient.

Kolejność wyszukiwań XYZ i LMN, odrzucenie starego wyniku oraz zamknięcie aktywnego żądania przy destroy

Gdzie umieścić obsługę błędu?

W powyższym rozwiązaniu catchError znajduje się wewnątrz switchMap. Błąd pojedynczego wyszukiwania staje się wartością stanu error. Zewnętrzny strumień zgłoszeń nadal może przyjąć następny kod. Laboratorium sprawdziło błąd dla ERR, późniejsze wysłanie NEW i poprawny wynik będący pustą listą.

Pusta lista to stan ready z zerem elementów, a nie awaria. Brak tego rozróżnienia często prowadzi do interfejsu, który po udanym wyszukiwaniu pokazuje użytkownikowi komunikat o błędzie. W ćwiczeniu zarówno query, jak i items należą do jednego obiektu stanu, więc wynik nie musi korzystać z aktualnie edytowanej wartości pola jako etykiety.

Nie wystarczy mechanicznie przenieść catchError na koniec i uznać problem za rozwiązany. Trzeba określić, który strumień kończy się po awarii oraz czy kolejny submit nadal dociera do usługi. Obroń położenie operatora wynikiem: po błędzie ERR następne zgłoszenie NEW nadal uruchamia wyszukiwanie.

Nie sprawdziliśmy automatycznych retry, limitu czasu, buforowania ani wielokrotnego kliknięcia tego samego kodu. Przed dodaniem retry ustal, czy wywołanie jest bezpieczne do ponowienia i czy użytkownik powinien widzieć czas oczekiwania oraz przycisk przerwania.

Kto kończy subskrypcję po zniszczeniu komponentu?

Oficjalny przewodnik takeUntilDestroyed opisuje automatyczne zakończenie subskrypcji związane z DestroyRef. Jawnie przekazujemy referencję pobraną przez inject(DestroyRef). Umieszczenie operatora po switchMap wiąże też aktywną wewnętrzną subskrypcję z czasem życia tego potoku.

Ostatnia sekwencja testu uruchamia wyszukiwanie END, niszczy aplikację z komponentem i dopiero potem próbuje dostarczyć wynik. Mock potwierdza zamknięcie aktywnej subskrypcji; zapisany stan nie zmienia się po spóźnionej odpowiedzi. Raport pozostaje w osobnym elemencie dokumentu, poza usuniętym komponentem.

To test sprzątania konkretnego wyszukiwania. Nie wykonaliśmy profilu pamięci i nie udowodniliśmy braku wszystkich wycieków. Nie sprawdziliśmy też zniszczenia w trakcie oczekiwania walidatora: ostatnia walidacja kończy się przed wysłaniem END. Własność subskrypcji utworzonej w serwisie singletonowym wymaga odrębnego rozważenia jego czasu życia.

Zmiana wymagania: wyszukiwanie przy każdym wpisanym znaku

Rozmówca może teraz poprosić o typeahead. Nie dopisuj tylko debounceTime do istniejącego submitu i nie ogłaszaj zadania zakończonym. Ustal, czy wyszukiwanie wymaga wcześniejszego potwierdzenia istnienia kodu, czy sam backend wyszukiwania może zwrócić brak dopasowań. Te warianty mają inny koszt oraz inną kolejność stanów. Podwójne sprawdzanie tej samej wartości nie wynika z konieczności używania Angulara.

W naszym formularzu poprawność potwierdza osobna kontrolowana usługa, a następnie użytkownik naciska Szukaj. Jeżeli przejście do automatycznego wyszukiwania ma zachować tę zasadę, nowy potok powinien powiązać odpowiedź walidatora z dokładną wartością wejścia. Odczyt aktualnego code.value w późnym callbacku może przypisać zgodę do innego kodu. Zapisz więc parę wartość–wynik i określ, co dzieje się po zmianie pola podczas oczekiwania.

Zastanów się również nad ponownym wpisaniem identycznego kodu. Czy ponowne zgłoszenie ma odświeżyć dane, wykorzystać cache, czy niczego nie robić? Samo distinctUntilChanged wybiera pewną politykę, nie stanowi uniwersalnej optymalizacji. W tym laboratorium ponowne wywołanie search dla valid może rozpocząć nowe wyszukiwanie; nie dodaliśmy deduplikacji ani cache. Nie dopisujemy ich do listy wykonanych kontroli.

Dopiero po tych decyzjach wybierz operator ograniczający częstotliwość i zapisz nowe testy. Przydatna sekwencja to trzy edycje przed końcem opóźnienia, odpowiedź starej walidacji po nowej edycji i zniszczenie komponentu podczas oczekiwania. Ustal też oczekiwaną etykietę wyświetlanego wyniku. Usunięcie starej listy oraz zamknięcie jej subskrypcji to osobne zachowania: można wykonać jedno bez drugiego. Ten rozszerzony wariant pozostaje propozycją do sprawdzenia, nie częścią naszych 22 zaliczonych kontroli.

Jak samodzielnie zweryfikować odpowiedź

Zacznij od zapisania kolejności zdarzeń, zanim przeczytasz implementację. Po krótkim AB liczba wywołań walidatora ma pozostać zero. Po ABC → ABD stara walidacja ma być zamknięta, a jej późna odpowiedź nie może rozstrzygnąć nowej wartości. Dopiero odpowiedź na bieżący kod decyduje o valid albo invalid.

Następnie zatwierdź poprawny XYZ, potem poprawny LMN. Najpierw dostarcz wynik LMN-1, następnie spóźniony XYZ. Oczekiwany zapis to ready: LMN — LMN-1. Ten porządek odróżnia test wyścigu od zwykłego sprawdzenia szczęśliwej ścieżki. Powtórz scenariusz z błędem i kolejnym zgłoszeniem, a na końcu zniszcz komponent podczas aktywnego wyszukiwania.

Zapisane 22 kontrole obejmują wskazane statusy, teardown, dwie obserwacje DOM i brak późnej zmiany po destroy. Nie obejmują rzeczywistego transportu HTTP, dostępności ani pełnego cyklu walidatora podczas destroy. Następne użyteczne przypadki to odpowiedź bez emisji, długie oczekiwanie, edycja podczas wyświetlania starego wyniku, przywrócenie formularza i klawiaturowe wysłanie przez Enter.

Pięć pytań PracHub do obrony rozwiązania

Tytuły prowadzą do anglojęzycznych ćwiczeń. Dwa dotyczą bezpośrednio Angulara; pozostałe rozwijają testowanie formularzy i diagnozowanie błędów interfejsu. Nie przedstawiamy ich jako dowodu częstotliwości pytań na polskich rozmowach.

Pełny tytuł ćwiczeniaCo sprawdzić po tym artykule
Describe Angular featuresWyjaśnij konkretną współpracę Reactive Forms, signals i RxJS zamiast wyliczać funkcje.
Debug an Angular UI from User ReportsOddziel objaw starego wyniku od hipotezy o subskrypcji.
Build a Credit Card Form With Validation and Linked Input FieldsPrzenieś rozumowanie o stanie pola i zależnościach; reguły danych będą inne.
Build and Validate Passport Form UIDodaj jawne stany błędu oraz testy granic formatu, bez kopiowania naszej reguły trzech liter.
Design Test Cases for UI Change: Ensuring Quality and FunctionalityZapisz kolejność odpowiedzi i oczekiwany DOM, także po usunięciu widoku.

Wybierz Debug an Angular UI from User Reports i przedstaw odpowiedź w trzech częściach: kontrakt, kolejność zdarzeń, dowód. Wersja frameworka oraz granica między odsubskrybowaniem a rzeczywistym anulowaniem pracy serwera powinny paść zanim nazwiesz poprawkę kompletną.

Sources and Further Reading


Comments (0)