Components & Templates Questions
Components are the fundamental building block of every Angular application, and interviewers lean heavily on them to gauge whether you understand the framework’s rendering model. Expect questions that move from the obvious (“what is a component?”) into subtle territory: lifecycle ordering, how parents and children talk to each other, and how the new control-flow syntax changed templates. This page works through the questions that come up most, with example answers using modern standalone components and signals.
What is a standalone component and why is it the default now?
A standalone component declares its own dependencies through its imports array instead of relying on an NgModule. Since Angular 17 the CLI scaffolds standalone components by default, and bootstrapApplication replaces the old AppModule. This removes a whole layer of indirection — you no longer hunt through modules to find where a directive was declared.
import { Component, signal } from '@angular/core';
@Component({
selector: 'app-counter',
standalone: true,
template: `
<button (click)="increment()">Clicked {{ count() }} times</button>
`,
})
export class CounterComponent {
count = signal(0);
increment() {
this.count.update((n) => n + 1);
}
}
Note:
standalone: trueis now the default in Angular 19, so you can omit it. Many interviewers still expect you to mention it explicitly.
Walk through the component lifecycle hooks in order
Interviewers love asking for the order, because it reveals whether you’ve actually debugged a rendering bug. The hooks fire in this sequence:
| Hook | When it runs |
|---|---|
ngOnChanges | Before ngOnInit and whenever an input changes (only if there are inputs) |
ngOnInit | Once, after the first ngOnChanges |
ngDoCheck | Every change-detection run |
ngAfterContentInit | Once, after projected content is initialized |
ngAfterContentChecked | After every check of projected content |
ngAfterViewInit | Once, after the component’s own view and children are initialized |
ngAfterViewChecked | After every check of the view |
ngOnDestroy | Just before the component is torn down |
A common follow-up: “Where do you query a @ViewChild?” The answer is ngAfterViewInit, because the view isn’t ready in ngOnInit. With signal-based queries you can read them earlier in a computed or effect.
How do components communicate?
This is almost guaranteed. The clean modern answer uses the signal-based input() and output() functions:
import { Component, input, output } from '@angular/core';
@Component({
selector: 'app-rating',
standalone: true,
template: `
<span>{{ label() }}: {{ value() }}</span>
<button (click)="bump()">+</button>
`,
})
export class RatingComponent {
label = input.required<string>();
value = input(0);
changed = output<number>();
bump() {
this.changed.emit(this.value() + 1);
}
}
The parent binds with property syntax and listens with event syntax:
<app-rating label="Quality" [value]="score" (changed)="onChanged($event)" />
Mention the alternatives so you sound complete: a shared service with a signal or Subject for sibling/unrelated components, and inject() to grab a parent component directly when there’s a tight hierarchy.
Explain Angular’s data binding types
There are four directions of binding, and being able to name all four crisply scores points:
<!-- Interpolation: component -> view -->
<p>{{ user().name }}</p>
<!-- Property binding: component -> view -->
<img [src]="avatarUrl()" [alt]="user().name" />
<!-- Event binding: view -> component -->
<button (click)="save()">Save</button>
<!-- Two-way binding: both directions -->
<input [(ngModel)]="query" />
Two-way binding is just sugar: [(ngModel)] expands to a [ngModel] property binding plus an (ngModelChange) event binding. With the model() signal you can build your own two-way bindable inputs without ngModel.
What is content projection and when do you use it?
Content projection lets a component render markup supplied by its parent through <ng-content>. It’s how you build reusable wrappers — cards, dialogs, layout shells — without the wrapper knowing what goes inside.
@Component({
selector: 'app-card',
standalone: true,
template: `
<section class="card">
<header><ng-content select="[card-title]" /></header>
<div class="body"><ng-content /></div>
</section>
`,
})
export class CardComponent {}
<app-card>
<h2 card-title>Invoices</h2>
<p>You have 3 unpaid invoices.</p>
</app-card>
The select attribute enables multi-slot projection — the <h2> lands in the header slot, everything else falls into the default slot. A good follow-up answer: projected content participates in ngAfterContentInit, not ngAfterViewInit.
What changed with the new control flow?
Angular 17 introduced built-in @if, @for, and @switch that replace the *ngIf, *ngFor, and *ngSwitch structural directives. They’re faster, need no imports, and @for requires a track expression.
@if (user(); as u) {
<p>Welcome, {{ u.name }}</p>
} @else {
<p>Please sign in</p>
}
@for (item of items(); track item.id) {
<li>{{ item.label }}</li>
} @empty {
<li>No items</li>
}
Welcome, Ada
Warning: forgetting
trackin@foris a build error, not a runtime warning — interviewers sometimes test whether you know it’s mandatory.
Best Practices
- Prefer standalone components and the signal-based
input()/output()/model()APIs for new code. - Keep templates thin: push logic into
computedsignals or methods rather than complex template expressions. - Always provide a stable
trackkey in@forto avoid unnecessary DOM re-creation. - Read
@ViewChildresults inngAfterViewInit, or use signal queries for earlier, reactive access. - Use multi-slot content projection to build flexible, reusable layout components.
- Clean up subscriptions in
ngOnDestroy(or prefertakeUntilDestroyed) to prevent leaks.