Angular essentials
Angular is the third framework interviewers name alongside React and Vue, and it is the one most often described from a 2018 memory. Modern Angular - standalone components, signals, the new control flow - is a different framework from the one with NgModules everywhere.
The shape of it
Angular is a framework, not a library: routing, HTTP, forms, testing and dependency injection ship with it and are expected to be used. That is the central difference from React, where each of those is a choice.
A component is a class with a decorator:
@Component({
selector: "app-invoice",
standalone: true,
template: `<h2>{{ total() | currency }}</h2>`,
})
export class InvoiceComponent {
total = signal(0);
}Three things are visible here that define the model: TypeScript is mandatory
rather than optional, the template is a separate language with its own syntax,
and standalone: true is the modern default.
Standalone components
Angular’s original unit of organisation was the NgModule - a declaration block listing every component, directive and pipe available to a template.
Standalone components, stable since Angular 15 and the default in new projects from 16 onward, removed that. A component declares its own imports:
@Component({
standalone: true,
imports: [CommonModule, RouterLink],
template: `<a routerLink="/home">Home</a>`,
})If asked about NgModules, the useful answer is that they were the old dependency-declaration mechanism and are no longer how new code is written.
Dependency injection
This is Angular’s most distinctive feature and the one a Python backend
engineer will recognise immediately: a hierarchical DI container, the same idea
as FastAPI’s Depends or a service container.
@Injectable({ providedIn: "root" })
export class InvoiceApi {
private http = inject(HttpClient);
list() {
return this.http.get<Invoice[]>("/api/invoices");
}
}providedIn: "root" makes it a singleton for the application. Providing it at
a component instead gives that component subtree its own instance, which is how
scoping works.
The payoff is the same as on the backend: swapping the real API for a fake in a test is a provider override, not a module mock. See Dependency Injection.
Change detection, and why signals arrived
Classic Angular used zone.js, which monkey-patches browser async APIs to know when something might have changed, then checks the component tree. It works without any annotation from you, and it over-checks: any timer or event triggers a pass over components that did not change.
OnPush narrowed that by only re-checking when inputs change by reference.
Signals replaced the problem rather than tuning it. A signal is a reactive value that records who read it, so a write can notify exactly those consumers.
count = signal(0);
double = computed(() => this.count() * 2);
increment() {
this.count.update(n => n + 1);
}Reading is a function call - count() - which is what lets Angular track the
dependency. computed is derived and cached; it recomputes only when something
it read has changed.
This is the same model as Vue’s reactivity and Solid’s signals, and it is moving Angular toward zoneless applications where change detection is driven by signal writes rather than by patched async APIs.
The new control flow
Templates gained built-in block syntax, replacing the structural directives:
@if (user(); as u) {
<p>{{ u.name }}</p>
} @else {
<p>Signed out</p>
}
@for (item of items(); track item.id) {
<li>{{ item.name }}</li>
}track is required in @for and is the equivalent of React’s key - without
a stable identity the list is re-created rather than moved.
RxJS: the part people struggle with
Angular’s HTTP client and router return Observables, so RxJS is not optional in a real codebase. An Observable is a stream you compose with operators.
search = this.query.pipe(
debounceTime(300),
distinctUntilChanged(),
switchMap(q => this.api.search(q)),
);switchMap is worth knowing by name: it cancels the previous inner request
when a new value arrives, which is exactly right for a search box and is the
canonical example of why streams beat promises for this problem.
The current direction is signals for component state and RxJS for genuine
event streams, with toSignal bridging the two. Saying that split shows you
have used recent Angular.
Related
Interview angle 7
- “How is Angular different from React?” - it is a framework rather than a library: routing, HTTP, forms, testing and dependency injection are included and expected, TypeScript is mandatory, and templates are a separate language. React leaves each of those to you.
- “What replaced NgModules?” - standalone components, stable from Angular 15 and the default since 16. A component declares its own imports instead of being registered in a module’s declarations block.
- “How does Angular’s DI work?” - a hierarchical injector.
providedIn: "root"gives an application singleton; providing at a component scopes an instance to that subtree. Tests override providers rather than mocking modules. - “What was wrong with zone.js?” - it patches async browser APIs to detect possible changes, then checks the component tree, so unrelated timers and events trigger checks of components that did not change.
OnPushnarrows it; signals remove the need for it. - “What is a signal?” - a reactive value that tracks its readers, so a write notifies exactly the consumers that read it. Reading is a call,
count(), which is how the dependency is recorded, andcomputedderives cached values. It is the same model as Vue’s reactivity and enables zoneless change detection. - “Why is
trackrequired in@for?” - it gives each row a stable identity so the DOM node is moved rather than rebuilt, the same role as React’skey. - “Where does RxJS still belong?” - genuine event streams and request cancellation, where
switchMapdiscards an in-flight request when a newer value arrives. Component state is moving to signals, withtoSignalbridging.