Na rozmowach z Angularem najczęściej pada kilkanaście pytań: czym jest komponent, jak działa wstrzykiwanie zależności, czym różni się Observable od Promise, kiedy odsubskrybować strumień, jak działają sygnały i czym jest change detection. Rekruter nie sprawdza definicji z pamięci, tylko to, czy rozumiesz, jak Angular odświeża widok i zarządza zależnościami. Dobra odpowiedź to jedno zdanie definicji i krótki przykład kodu.
Rozmowa techniczna z Angularem rzadko jest egzaminem z całej dokumentacji. Najczęściej to te same kilkanaście tematów, przy których rekruter sprawdza, czy rozumiesz, jak Angular działa pod spodem, a nie czy pamiętasz nazwy dekoratorów. Poniżej znajdziesz pytania, które wracają na rozmowach na stanowisko juniora, razem z tym, jak na nie odpowiadać. Jeśli dopiero poznajesz ten framework, zacznij od artykułu o tym, czym jest Angular i kiedy warto go wybrać, a ten tekst potraktuj jako listę kontrolną. Angular jest napisany w TypeScripcie, więc przy rekrutacji prawie zawsze padają też pytania rekrutacyjne z TypeScript i z JavaScript.
Przykłady z tego artykułu zbudowaliśmy w projekcie wygenerowanym przez Angular CLI (Angular 22.2, TypeScript 6.0, RxJS 7.8), dlatego komunikat błędu w tekście jest faktycznym komunikatem kompilatora, a kod przeszedł ng build.
Jak odpowiadać na pytania rekrutacyjne z Angulara?
Najlepiej w trzech krokach: jedno zdanie definicji, krótki przykład (kilka linijek kodu albo sytuacja z projektu) i informacja, kiedy to ma znaczenie w praktyce. Taka odpowiedź zajmuje 30–60 sekund i pokazuje, że rozumiesz mechanizm, a nie wkułeś formułkę. Jeśli czegoś nie wiesz, powiedz, co wiesz, i rozumuj na głos. Rekruterzy oceniają sposób myślenia, a nie tylko wynik.
Warto też wiedzieć, że Angular zmienił się mocno w ostatnich latach. Starsze projekty używają modułów (NgModule), dyrektyw *ngIf i *ngFor oraz klas z konstruktorem, nowsze: komponentów samodzielnych, sygnałów i bloków @if / @for. Rekruter często sprawdza, czy znasz oba style, bo w pracy trafisz na jeden i drugi. Dlatego przy kluczowych pytaniach podaję obie wersje.
Pytania są ułożone od podstaw do tematów, które częściej pojawiają się przy ofertach na mid-poziom.
Podstawy Angulara
Czym jest Angular i czym różni się od biblioteki takiej jak React?
Angular to kompletny framework do budowy aplikacji webowych, rozwijany przez zespół Google i napisany w TypeScripcie. Dostarcza w jednym pakiecie komponenty, routing, formularze, klienta HTTP, wstrzykiwanie zależności i narzędzie wiersza poleceń (Angular CLI). React jest biblioteką do widoków, więc resztę (routing, formularze, zarządzanie stanem) dobierasz sam. Angular narzuca strukturę projektu, co ułatwia pracę w dużych zespołach, ale oznacza więcej pojęć do poznania na starcie. Szerzej porównujemy je w artykule o tym, czym jest Angular.
Z czego składa się komponent w Angularze?
Komponent to klasa TypeScript z dekoratorem @Component, który podaje selektor (nazwę znacznika HTML), szablon i style. Klasa trzyma dane i logikę, szablon wyświetla je użytkownikowi. Komponenty składa się w drzewo, w którym app-root jest korzeniem.
import { Component, signal } from '@angular/core';
@Component({
selector: 'app-hello',
template: `<h2>Cześć, {{ name() }}!</h2>`,
})
export class Hello {
name = signal('Ala');
}
Co to jest komponent samodzielny (standalone) i czym różni się od NgModule?
Komponent samodzielny (standalone component) sam deklaruje, czego używa w szablonie, w tablicy imports. Dawniej każdy komponent musiał należeć do modułu (NgModule), który zbierał komponenty, importy i dostawców w jednym miejscu. W nowszych wersjach Angulara komponenty są samodzielne domyślnie, więc nie piszesz już standalone: true, a moduły zostały jako starszy sposób organizacji kodu. W starszych projektach nadal je zobaczysz, więc dobrze wiedzieć, jak działają declarations, imports i providers.
Czym są dekoratory w Angularze?
Dekorator to funkcja ze znakiem @, która dopisuje klasie lub jej polu metadane dla Angulara. Najczęściej spotkasz @Component, @Injectable i @Directive. W starszym kodzie dochodzą @Input() i @Output() na polach klasy, a w nowszym zastępują je funkcje input() i output(). Dekoratory pochodzą z TypeScript, ale to Angular nadaje im znaczenie.
Jakie są rodzaje bindowania danych w Angularze?
Są cztery sposoby łączenia klasy z szablonem:
| Zapis | Nazwa | Kierunek | Przykład |
|---|---|---|---|
{{ wartość }} | interpolacja | klasa → szablon | <p>{{ name() }}</p> |
[właściwość] | property binding | klasa → szablon | <img [src]="url"> |
(zdarzenie) | event binding | szablon → klasa | <button (click)="save()"> |
[(ngModel)] | dwukierunkowe | w obie strony | <input [(ngModel)]="name"> |
Dwukierunkowe bindowanie to po prostu skrót: [ngModel] plus (ngModelChange). Warto to powiedzieć na rozmowie, bo pokazuje, że rozumiesz, co się za nim kryje.
Czym różni się interpolacja od property binding?
Interpolacja wstawia wartość jako tekst, a property binding ustawia właściwość elementu DOM i może przekazać dowolny typ, np. obiekt lub wartość logiczną. Dlatego [disabled]="isSaving()" ustawia właściwość disabled, a zapis disabled="{{ isSaving() }}" zamieniłby wartość na napis. Przy atrybutach tekstowych oba zapisy dają ten sam efekt.
Jak w Angularze wyświetla się warunkowo i listy?
W nowszych wersjach służą do tego bloki @if i @for wbudowane w szablon, a w starszym kodzie dyrektywy *ngIf i *ngFor.
@if (count() > 2) {
<p>Dużo!</p>
}
@for (item of items(); track item.id) {
<li>{{ item.name }}</li>
} @empty {
<li>brak elementów</li>
}
W bloku @for track jest obowiązkowe. Wskazuje, co identyfikuje element listy (zwykle id), dzięki czemu Angular przy zmianie danych przestawia istniejące elementy DOM, zamiast tworzyć je od nowa. W starszym *ngFor odpowiada temu opcja trackBy.
Komponenty i komunikacja
Jak przekazać dane z komponentu rodzica do dziecka i z powrotem?
Rodzic przekazuje dane przez właściwość wejściową (input), a dziecko informuje rodzica zdarzeniem wyjściowym (output).
import { Component, input, output, signal } from '@angular/core';
@Component({
selector: 'app-counter',
template: `
<p>{{ label() }}: {{ count() }}</p>
<button (click)="increment()">+1</button>
`,
})
export class Counter {
label = input.required<string>();
changed = output<number>();
count = signal(0);
increment() {
this.count.update((c) => c + 1);
this.changed.emit(this.count());
}
}
Rodzic użyje go tak: <app-counter label="Kliknięcia" (changed)="onChange($event)" />. W starszym kodzie to samo robią dekoratory @Input() i @Output() z EventEmitter. Zasada jest ta sama: dane płyną w dół, zdarzenia w górę.
Jak wykryć zmianę wartości wejściowej?
W starszym podejściu służy do tego hook ngOnChanges, który dostaje obiekt SimpleChanges z poprzednią i nową wartością. W nowszym input() jest sygnałem, więc zmianę obsługujesz przez computed() lub effect(), które automatycznie reagują na jego nową wartość.
Jakie są hooki cyklu życia komponentu i które trzeba znać?
Hooki cyklu życia (lifecycle hooks) to metody, które Angular wywołuje w określonych momentach istnienia komponentu. Na rozmowie wystarczy znać kolejność i zastosowanie głównych:
| Hook | Kiedy | Do czego służy |
|---|---|---|
constructor | przy tworzeniu klasy | wstrzyknięcie zależności, bez logiki |
ngOnChanges | gdy zmieni się wejście | reakcja na nowe wartości @Input() |
ngOnInit | raz, po pierwszym ustawieniu wejść | pobranie danych, inicjalizacja |
ngAfterViewInit | po wyrenderowaniu widoku | praca z elementami DOM i dziećmi widoku |
ngOnDestroy | tuż przed usunięciem | sprzątanie: subskrypcje, timery |
Czym różni się constructor od ngOnInit?
Konstruktor to mechanizm TypeScript, który tworzy obiekt klasy. Angular wywołuje go, gdy tworzy komponent, jeszcze przed ustawieniem właściwości wejściowych. ngOnInit to hook Angulara, wywoływany raz, gdy wejścia są już dostępne. Dlatego w konstruktorze zostawiasz tylko wstrzyknięcie zależności i prostą inicjalizację pól, a pobranie danych i logikę, która korzysta z @Input(), umieszczasz w ngOnInit. To jedno z najczęściej zadawanych pytań, bo od razu pokazuje, czy odróżniasz język od frameworka.
Czym różni się komponent od dyrektywy?
Komponent to dyrektywa z własnym szablonem. Dyrektywa nie ma szablonu: dodaje zachowanie istniejącemu elementowi. Wyróżniamy dyrektywy atrybutowe, które zmieniają wygląd lub zachowanie elementu (np. podświetlenie po najechaniu myszką), i dyrektywy strukturalne, które zmieniają strukturę DOM (starsze *ngIf, *ngFor). Przykład z praktyki: komponent to cała karta produktu, dyrektywa to atrybut dodający do dowolnego elementu etykietkę z podpowiedzią (tooltip).
Sygnały, change detection i wydajność
Co to są sygnały (signals) i po co je wprowadzono?
Sygnał (signal) to opakowanie wokół wartości, które potrafi powiadomić Angulara, że wartość się zmieniła. Odczytujesz go jak funkcję (count()), zapisujesz przez set() lub update(), a wartości pochodne liczysz w computed(). Zaleta: Angular wie dokładnie, które fragmenty szablonu zależą od danego sygnału, więc może odświeżać tylko je.
import { computed, effect, signal } from '@angular/core';
const count = signal(0);
const double = computed(() => count() * 2);
effect(() => console.log('count =', count()));
count.set(1);
count.update((c) => c + 1); // double() === 4
Czym różni się computed od effect?
computed() zwraca nową wartość wyliczoną z innych sygnałów, jest leniwy i zapamiętuje wynik, dopóki zależności się nie zmienią. effect() nie zwraca wartości, tylko wykonuje efekt uboczny (np. zapis do localStorage, log) za każdym razem, gdy zmieni się sygnał, który odczytał. Prosta zasada: jeśli liczysz wartość do wyświetlenia, użyj computed, a effect zostaw na to, co dzieje się poza szablonem.
Co to jest change detection i jak działa w Angularze?
Change detection (wykrywanie zmian) to mechanizm, który sprawdza, czy dane w komponentach się zmieniły, i aktualizuje DOM. Domyślnie Angular po każdym zdarzeniu (kliknięcie, odpowiedź HTTP, timer) przechodzi przez całe drzewo komponentów od korzenia i porównuje wartości użyte w szablonach. W małej aplikacji nie jest to problem, w dużej może spowalniać interfejs, dlatego stosuje się strategię OnPush.
Czym jest strategia OnPush i kiedy ją stosować?
Z ChangeDetectionStrategy.OnPush Angular sprawdza komponent tylko wtedy, gdy zmieni się jego wejście (nowa referencja), wystąpi zdarzenie w tym komponencie, zmieni się odczytany sygnał albo strumień z async zasygnalizuje nową wartość. Dzięki temu pomija całe gałęzie drzewa, które nie mają powodu się zmienić. Haczyk: jeśli zmutujesz obiekt przekazany jako wejście (np. user.name = 'Ola'), referencja się nie zmienia i widok się nie odświeży. Dlatego przy OnPush tworzy się nowy obiekt ({ ...user, name: 'Ola' }) albo używa sygnałów.
Skąd się bierze błąd ExpressionChangedAfterItHasBeenCheckedError?
Angular w trybie deweloperskim sprawdza każde wyrażenie w szablonie dwukrotnie. Jeśli za drugim razem wartość jest inna niż za pierwszym, znaczy to, że zmieniła się w trakcie samego renderowania (np. dziecko w hooku zmienia dane, które wyświetla rodzic), i rzuca ten błąd. Rozwiązanie to przeniesienie zmiany w miejsce, w którym nie dzieje się to w trakcie sprawdzania: do ngOnInit, do osobnego przebiegu, albo przejście na sygnały. Opakowanie zmiany w setTimeout bywa tylko łatką, która ukrywa problem z przepływem danych.
Serwisy i wstrzykiwanie zależności
Co to jest wstrzykiwanie zależności (dependency injection) i po co jest w Angularze?
Wstrzykiwanie zależności (dependency injection, DI) polega na tym, że klasa nie tworzy sama obiektów, których potrzebuje, tylko dostaje je z zewnątrz od Angulara. Dzięki temu logikę (np. pobieranie danych) wydzielasz do serwisu, a komponent tylko ją wywołuje. Ułatwia to testy, bo w teście podstawiasz atrapę serwisu, i pozwala współdzielić jedną instancję między komponentami.
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({ providedIn: 'root' })
export class TodoService {
private http = inject(HttpClient);
load() {
return this.http.get<Todo[]>('/api/todos');
}
}
Jak wstrzyknąć serwis: przez konstruktor czy przez inject()?
Oba sposoby działają. Starszy zapis wstrzykuje przez parametr konstruktora (constructor(private http: HttpClient) {}), nowszy przez funkcję inject() przypisaną do pola klasy. inject() jest krótsze, działa też poza klasami (np. w funkcyjnych guardach) i nie psuje się przy dziedziczeniu. Na rozmowie najlepiej powiedzieć, że znasz obie wersje i że w nowym kodzie używa się inject().
Co oznacza providedIn: 'root'?
Że Angular utworzy jedną instancję serwisu (singleton) dla całej aplikacji i udostępni ją wszędzie bez dodatkowej rejestracji. Alternatywą jest zarejestrowanie serwisu w providers konkretnego komponentu: wtedy każdy egzemplarz komponentu dostaje własną kopię serwisu. Ten drugi sposób przydaje się, gdy serwis trzyma stan, który nie ma być współdzielony.
Skąd się bierze błąd „No provider for HttpClient”?
Z tego, że Angular nie wie, jak utworzyć HttpClient, bo nie dodano go do konfiguracji aplikacji. W nowszych projektach dodajesz provideHttpClient() w app.config.ts, a w starszych importujesz HttpClientModule w module. To dobry przykład pytania, które sprawdza, czy rozumiesz DI: błąd mówi wprost, że brakuje rejestracji dostawcy (provider), a nie że coś jest nie tak z kodem serwisu.
RxJS i asynchroniczność
Czym różni się Observable od Promise?
Promise zwraca jedną wartość w przyszłości i zaczyna się wykonywać od razu po utworzeniu. Observable może wyemitować wiele wartości w czasie, jest leniwy (nic się nie dzieje, dopóki ktoś się nie zasubskrybuje) i można go anulować przez odsubskrybowanie. Każda subskrypcja uruchamia go od nowa. Zobacz to na krótkim doświadczeniu z RxJS 7.8:
import { Observable } from 'rxjs';
const promise = new Promise((resolve) => {
console.log('promise start');
resolve(1);
});
const observable = new Observable((subscriber) => {
console.log('observable start');
subscriber.next(1);
subscriber.complete();
});
console.log('po utworzeniu');
observable.subscribe((v) => console.log('sub1', v));
observable.subscribe((v) => console.log('sub2', v));
Wynik: promise start pojawia się od razu, po utworzeniu zaraz po nim, a observable start dopiero przy każdej subskrypcji, czyli dwa razy. W praktyce zapytanie HttpClient zwraca Observable, więc bez subscribe (albo async w szablonie) żądanie w ogóle nie wyjdzie.
Czym różni się Subject od BehaviorSubject?
Subject to strumień, do którego możesz sam wysyłać wartości, a nowy subskrybent dostaje tylko te, które pojawią się po jego zapisaniu. BehaviorSubject zawsze ma bieżącą wartość: wymaga wartości początkowej, a nowy subskrybent od razu dostaje ostatnią. Dlatego BehaviorSubject nadaje się do trzymania stanu w serwisie (np. aktualny użytkownik), a zwykły Subject do zdarzeń. Sprawdzone: po wysłaniu 5 i późniejszym zapisaniu się BehaviorSubject zwrócił 5, a Subject dopiero kolejną wartość 6.
import { BehaviorSubject, Observable } from 'rxjs';
private subject = new BehaviorSubject<Todo[]>([]);
todos$: Observable<Todo[]> = this.subject.asObservable();
Warto dodać, że na zewnątrz serwisu wystawia się asObservable(), żeby inne klasy mogły strumień tylko czytać, a nie wysyłać do niego dane.
Kiedy trzeba odsubskrybować strumień i jak to zrobić?
Strumienie, które same się nie kończą (np. interval, zdarzenia, BehaviorSubject), trzeba odsubskrybować, gdy komponent znika. Inaczej subskrypcja żyje dalej i powstaje wyciek pamięci (memory leak). Strumienie HttpClient kończą się same po odpowiedzi i zwykle nie wymagają sprzątania. Trzy podejścia, od najprostszego:
asyncw szablonie.todos$ | asyncsubskrybuje i odsubskrybowuje za ciebie.takeUntilDestroyed(). Operator z@angular/core/rxjs-interop, który kończy strumień razem z komponentem lub serwisem.- Ręcznie w
ngOnDestroy. ZapisujeszSubscriptioni wołaszunsubscribe(). Działa, ale łatwo o niej zapomnieć.
import { DestroyRef, inject } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { interval } from 'rxjs';
private destroyRef = inject(DestroyRef);
poll() {
interval(1000)
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => this.load());
}
Do czego służy async pipe?
Pipe async subskrybuje Observable (lub Promise) bezpośrednio w szablonie, wyświetla ostatnią wartość i sam odsubskrybowuje się, gdy komponent zniknie. Dzięki niemu nie piszesz subscribe i ngOnDestroy w klasie, a przy OnPush dodatkowo informuje Angulara o nowej wartości. W szablonie wygląda to tak: @for (t of todos$ | async; track t.id). Jeśli ten sam strumień jest używany w kilku miejscach, pamiętaj, że każdy async to osobna subskrypcja.
Które operatory RxJS trzeba znać?
Na poziom juniora wystarczą cztery rodziny. map i filter przekształcają i filtrują wartości, tap pozwala wykonać efekt uboczny bez zmiany strumienia, a switchMap przełącza się na nowy strumień i anuluje poprzedni. switchMap jest szczególnie ważny przy wyszukiwarce: gdy użytkownik wpisze kolejną literę, stare zapytanie zostaje anulowane i nie nadpisze nowszego wyniku. Do tego debounceTime, który czeka chwilę po ostatnim znaku, zanim wyśle zapytanie.
Formularze, routing i HTTP
Czym różni się formularz szablonowy od reaktywnego?
W formularzu szablonowym (template-driven) logikę definiujesz w HTML przez ngModel i atrybuty walidacji, więc działa dobrze przy prostych formularzach. W reaktywnym (reactive) budujesz model w klasie (FormGroup, FormControl), a szablon tylko się do niego podpina. Daje to pełną kontrolę nad walidacją, łatwiejsze testy i dynamiczne pola, dlatego w większych aplikacjach wybiera się podejście reaktywne.
import { Component, inject } from '@angular/core';
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
@Component({
selector: 'app-form',
imports: [ReactiveFormsModule],
template: `
<form [formGroup]="form" (ngSubmit)="send()">
<input formControlName="email" />
@if (form.controls.email.errors?.['required']) {
<small>Pole jest wymagane</small>
}
</form>
`,
})
export class FormCmp {
form = inject(FormBuilder).nonNullable.group({
email: ['', [Validators.required, Validators.email]],
});
send() {
console.log(this.form.getRawValue());
}
}
Skąd się bierze błąd „Can't bind to 'ngModel' since it isn't a known property of 'input'”?
Z tego, że komponent nie zna dyrektywy ngModel, bo nie zaimportowano FormsModule. Błąd o tym samym schemacie dotyczy formGroup i formControl, gdy brakuje ReactiveFormsModule. Sprawdziliśmy to w projekcie: komponent z <input [(ngModel)]="x" /> bez importu kończy się błędem kompilacji NG8002 o treści „Can't bind to 'ngModel' since it isn't a known property of 'input'”. Naprawa to dopisanie FormsModule do tablicy imports komponentu (lub do modułu w starszym projekcie). To jedno z najczęściej oglądanych pytań o Angulara na Stack Overflow (ponad 1,8 mln wyświetleń), więc na rozmowie warto znać jego przyczynę.
Jak działa routing w Angularze i czym jest lazy loading?
Routing przypisuje adresy URL do komponentów, które wyświetla <router-outlet>. Lazy loading (leniwe ładowanie) oznacza, że kod danej części aplikacji pobiera się dopiero, gdy użytkownik wejdzie na jej adres, dzięki czemu pierwsze załadowanie strony jest szybsze.
import { Routes } from '@angular/router';
export const routes: Routes = [
{
path: 'admin',
loadComponent: () => import('./counter').then((m) => m.Counter),
},
];
Co to jest guard i do czego służy?
Guard to funkcja, która decyduje, czy użytkownik może wejść na daną trasę. CanActivateFn zwraca true (wpuszczamy) albo UrlTree, który przekierowuje gdzie indziej, np. na stronę logowania. W nowszych wersjach guardy są zwykłymi funkcjami i korzystają z inject(), a dawniej były klasami z interfejsem CanActivate.
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
export const authGuard: CanActivateFn = () => {
const auth = inject(AuthService);
const router = inject(Router);
return auth.isLoggedIn() ? true : router.parseUrl('/login');
};
Pamiętaj, że guard poprawia komfort użytkownika, ale nie jest zabezpieczeniem: prawdziwą kontrolę dostępu musi zrobić serwer.
Do czego służą interceptory HTTP?
Interceptor to funkcja, która przechwytuje każde żądanie HttpClient i odpowiedź. Używa się go do rzeczy wspólnych dla wszystkich zapytań: dodania nagłówka Authorization z tokenem, obsługi błędów (np. wylogowania po 401) albo wskaźnika ładowania. Dzięki niemu nie powtarzasz tego samego kodu w każdym serwisie.
Testy i praktyka
Jak testuje się komponent w Angularze?
Do testów jednostkowych służy TestBed, który tworzy komponent w środowisku testowym wraz z jego zależnościami. Zależności zastępuje się atrapami (np. serwis HTTP podmieniony na obiekt z danymi testowymi), dzięki czemu test nie wykonuje prawdziwych zapytań. Na poziom juniora wystarczy wiedzieć, po co jest TestBed, jak podmienić serwis w providers i jak wywołać fixture.detectChanges(), żeby odświeżyć widok.
Jakie zadania z kodem pojawiają się na rozmowach z Angularem?
Najczęściej to małe aplikacje do zrobienia w godzinę lub w domu:
- lista zadań z dodawaniem i usuwaniem (komponenty, bindowanie,
@for), - wyszukiwarka z zapytaniem do API (serwis,
HttpClient,debounceTime,switchMap), - formularz z walidacją (formularze reaktywne),
- dwie podstrony z nawigacją i ochroną jednej z nich (routing, guard).
Poza kodem prosi się też o poprawienie istniejącego fragmentu: znalezienie wycieku pamięci przez nieodsubskrybowany strumień albo wyjaśnienie, dlaczego widok się nie odświeża.
Co powie kompilator? Mini-test na rozgrzewkę
Sprawdź, czy bez uruchamiania kodu potrafisz powiedzieć, co się stanie.
1. Komponent używa [(ngModel)], ale w imports nie ma FormsModule. Błąd kompilacji NG8002: „Can't bind to 'ngModel' since it isn't a known property of 'input'”. Trzeba zaimportować FormsModule.
2. Dwa razy wołasz subscribe na tym samym Observable z HttpClient. Wysyłane są dwa osobne zapytania, bo Observable jest leniwy i każda subskrypcja uruchamia go od nowa.
3. Zmieniasz pole obiektu przekazanego do komponentu z OnPush (user.name = 'Ola'). Widok się nie odświeży, bo referencja obiektu jest ta sama. Przekaż nowy obiekt albo użyj sygnału.
Jak przygotować się do rozmowy z Angularem?
Samo czytanie odpowiedzi nie wystarczy, bo pytania uzupełniające szybko pokazują, czy rozumiesz temat. Skuteczniej działa zbudowanie małej aplikacji (lista zadań z wyszukiwarką i jedną chronioną trasą) i głośne tłumaczenie swoich decyzji komuś innemu, na przykład mentorowi. Dla porządku:
| Obszar | Co umieć wyjaśnić własnymi słowami |
|---|---|
| Podstawy | komponent, dekoratory, bindowanie, @if / @for, standalone vs NgModule |
| Komunikacja | input / output, hooki cyklu życia, constructor vs ngOnInit |
| Sygnały i wydajność | signal, computed, effect, change detection, OnPush |
| DI i serwisy | @Injectable, providedIn: 'root', inject(), błąd „No provider” |
| RxJS | Observable vs Promise, Subject vs BehaviorSubject, odsubskrybowanie, async pipe, switchMap |
| Formularze i routing | szablonowe vs reaktywne, lazy loading, guard, interceptor |
Na czym juniorzy najczęściej tracą punkty?
Nie na trudnych tematach, tylko na trzech drobiazgach, które wychodzą dopiero przy pytaniach uzupełniających:
- Subskrypcje bez sprzątania. Kto pisze
subscribei nie wie, kiedy ją zakończyć, ten zostawia wyciek pamięci. Pokaż, że znaszasync,takeUntilDestroyedingOnDestroy. - Mylenie
constructorzngOnInit. Odpowiedź „to to samo” kończy temat. Wyjaśnij, że wejścia są dostępne dopiero wngOnInit. - Mutowanie danych przy
OnPush. Zmiana pola w istniejącym obiekcie nie odświeży widoku. Powiedz, że w takim przypadku tworzysz nowy obiekt albo korzystasz z sygnałów.
Jak brzmi dobra odpowiedź, a jak słaba?
Weźmy pytanie o Observable i Promise:
| Odpowiedź | Dlaczego | |
|---|---|---|
| Słaba | „Observable to taki bardziej rozbudowany Promise.” | Brak różnicy w zachowaniu. Pytanie uzupełniające („co się stanie, jeśli nikt się nie zasubskrybuje?”) od razu to pokaże. |
| Dobra | „Promise daje jedną wartość i startuje od razu. Observable może emitować wiele wartości, jest leniwy i uruchamia się przy każdej subskrypcji, więc w Angularze trzeba pamiętać o subskrypcji albo async w szablonie.” | Jest różnica w zachowaniu, konsekwencja i zastosowanie. |
Ten sam schemat działa przy każdym pytaniu z tego artykułu: definicja, mechanizm, przykład użycia.
Co zrobić, gdy nie znasz odpowiedzi?
Nie zgaduj i nie milcz. Powiedz, co wiesz na pewno, a potem rozumuj na głos, np.: „Nie pamiętam nazwy tego operatora RxJS, ale wiem, że szukam takiego, który anuluje poprzednie zapytanie, więc sprawdziłbym to w dokumentacji.” Taka odpowiedź pokazuje, że potrafisz sobie poradzić w pracy, a umiejętność znalezienia odpowiedzi jest w codziennej pracy ważniejsza niż pamięć do szczegółów.
Jeśli dopiero planujesz ścieżkę nauki, pamiętaj, że w naszym kursie frontendu uczymy Reacta, bo jest popularniejszy na rynku pracy dla początkujących. Angular wybierz, gdy wymaga go konkretna firma, a przygotowanie do rozmowy z Reactem znajdziesz w pytaniach rekrutacyjnych z React. Ogólny przebieg całego procesu opisujemy w poradniku jak przygotować się do rekrutacji na juniora.


