Angular interviews test component architecture, dependency injection, RxJS, reactive patterns, and performance optimisation. This guide covers the 50 most common questions — with concise answers and code examples updated for Angular 17+.
Quick reference
| Topic | Most asked questions |
|---|---|
| Architecture | Components, modules, standalone, change detection |
| Templates | Directives, pipes, two-way binding, new control flow |
| Services & DI | Dependency injection, providers, hierarchical injectors |
| RxJS & Signals | Observables, subjects, signals, toSignal/toObservable |
| Routing | Lazy loading, guards, resolvers, functional guards |
| Performance | OnPush, trackBy, defer, preloading, SSR |
| Testing | TestBed, ComponentFixture, MockMvc, harnesses |
Architecture
1. What is Angular and how is it different from React?
Angular is a full framework from Google — it includes routing, HTTP client, forms, DI, testing utilities, and CLI out of the box. React is a UI library that requires assembling a stack from separate packages.
| Angular | React | |
|---|---|---|
| Type | Full framework | UI library |
| Language | TypeScript (required) | JavaScript/TypeScript |
| Data binding | Two-way (ngModel) | One-way by default |
| State | Signals + RxJS | useState + context |
| Learning curve | Steeper | Gentler |
| Opinionation | High | Low |
2. What is a component in Angular?
A component is the fundamental UI building block. It consists of:
- Class — logic and data
- Template — HTML view
- Styles — scoped CSS
- Metadata —
@Componentdecorator
@Component({
selector: 'app-user',
standalone: true,
imports: [NgIf, DatePipe],
template: `
<div *ngIf="user">
<h2>{{ user.name }}</h2>
<p>Joined: {{ user.createdAt | date:'mediumDate' }}</p>
</div>
`,
})
export class UserComponent {
@Input() user?: User;
}
3. What is the difference between standalone components and NgModules?
NgModules (traditional) group components, directives, and pipes into cohesive units and declare their imports/exports. Standalone components (Angular 14+, default from Angular 17) declare their own dependencies via the imports array — no NgModule needed.
// Standalone (modern, default in Angular 17)
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, CommonModule],
templateUrl: './app.component.html',
})
export class AppComponent {}
// NgModule (legacy style)
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, RouterModule],
bootstrap: [AppComponent],
})
export class AppModule {}
Angular 17+ bootstraps with bootstrapApplication(AppComponent, appConfig) instead of platformBrowserDynamic().bootstrapModule(AppModule).
4. What is change detection in Angular?
Change detection is the mechanism Angular uses to keep the view in sync with the model. Angular checks whether component data has changed and updates the DOM accordingly.
Two strategies:
| Strategy | Behaviour |
|---|---|
Default |
Checks every component on every event/timer/async op |
OnPush |
Checks only when: @Input() reference changes, an event originates in the component, an async pipe emits, or markForCheck() is called manually |
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
// ...
})
export class ProductCardComponent {
@Input() product!: Product; // must pass new reference to trigger check
}
5. What is Angular's dependency injection system?
Angular has a hierarchical DI system. Injectors form a tree that mirrors the component tree. When a component requests a service, Angular walks up the injector tree until it finds a provider.
// Provided in root — singleton for the whole app
@Injectable({ providedIn: 'root' })
export class AuthService { ... }
// Provided at component level — new instance per component subtree
@Component({
providers: [CartService], // isolated instance
})
export class CartComponent {
constructor(private cart: CartService) {}
}
// Modern: inject() function (usable in constructors & initializers)
export class ProductComponent {
private http = inject(HttpClient);
}
Templates & Directives
6. What are Angular directives and what are the three types?
Directives extend HTML behaviour. There are three types:
| Type | Purpose | Example |
|---|---|---|
| Component | Directive with a template | <app-card> |
| Structural | Changes DOM structure | *ngIf, *ngFor, @if, @for |
| Attribute | Changes appearance/behaviour | [ngClass], [ngStyle], custom [appHighlight] |
// Custom attribute directive
@Directive({ selector: '[appHighlight]', standalone: true })
export class HighlightDirective {
@HostListener('mouseenter') onEnter() {
this.el.nativeElement.style.backgroundColor = 'yellow';
}
@HostListener('mouseleave') onLeave() {
this.el.nativeElement.style.backgroundColor = '';
}
constructor(private el: ElementRef) {}
}
7. What is the difference between *ngIf and @if (Angular 17)?
Angular 17 introduced a built-in control flow syntax (@if, @for, @switch) that replaces *ngIf / *ngFor / *ngSwitch. The new syntax is part of the template language — no import of NgIf or CommonModule needed.
<!-- Legacy -->
<div *ngIf="isLoggedIn; else loginBlock">Welcome</div>
<ng-template #loginBlock>Please log in</ng-template>
<!-- Angular 17+ built-in control flow -->
@if (isLoggedIn) {
<div>Welcome</div>
} @else {
<div>Please log in</div>
}
@for also requires a track expression (replaces trackBy):
@for (item of items; track item.id) {
<li>{{ item.name }}</li>
} @empty {
<li>No items</li>
}
8. What is two-way data binding and how does ngModel work?
Two-way binding keeps the view and the model in sync in both directions. [(ngModel)] is syntactic sugar for [ngModel] (property binding) + (ngModelChange) (event binding):
<!-- These are equivalent -->
<input [(ngModel)]="username" />
<input [ngModel]="username" (ngModelChange)="username = $event" />
ngModel requires FormsModule in the component's imports. For reactive forms, use formControlName with ReactiveFormsModule instead.
9. What is the difference between [class], [ngClass], and class?
<!-- Static class — always present -->
<div class="card">
<!-- Single dynamic class (expression must be truthy) -->
<div [class.active]="isActive">
<!-- Object/array binding for multiple classes -->
<div [ngClass]="{ active: isActive, disabled: isDisabled }">
<div [ngClass]="['card', isActive ? 'active' : '']">
<!-- Angular 17: same as [ngClass], no import needed -->
<div [class]="{ active: isActive }">
Prefer [class.foo] for single classes and [ngClass] / [class] for conditional sets.
10. What are Angular pipes and how do you create a custom pipe?
Pipes transform template values: {{ value | pipeName:arg1:arg2 }}.
Built-in pipes: date, currency, decimal, percent, uppercase, lowercase, titlecase, json, slice, async, keyvalue.
// Custom pipe: truncates a string
@Pipe({ name: 'truncate', standalone: true, pure: true })
export class TruncatePipe implements PipeTransform {
transform(value: string, limit = 100, ellipsis = '…'): string {
return value.length > limit ? value.slice(0, limit) + ellipsis : value;
}
}
Pure vs impure pipes:
pure: true(default) — called only when input reference changes. Efficient.pure: false— called on every change detection cycle. Use sparingly (e.g.asyncpipe is impure).
Services & Dependency Injection
11. What is providedIn: 'root' and when would you NOT use it?
providedIn: 'root' registers the service with the root injector, creating a singleton shared across the entire app and enabling tree-shaking (unused services are removed from the bundle).
Avoid it when you need:
- Lazy-module-scoped singletons — use
providedIn: SomeModuleor provide in the lazy route config - Per-component instances — provide in
@Component({ providers: [MyService] }) - Multiple independent instances — provide at different component levels
12. What is the difference between @Injectable, @Component, @Directive, and @Pipe?
All are Angular decorators that add metadata:
| Decorator | Used on | Makes class... |
|---|---|---|
@Injectable |
Service class | Injectable via DI |
@Component |
Component class | Component with template |
@Directive |
Directive class | DOM behaviour modifier |
@Pipe |
Pipe class | Template value transformer |
@Component and @Directive implicitly mark the class as injectable.
13. How do you share data between sibling components?
Options, from simplest to most powerful:
- Lift state to a common parent — parent passes data via
@Input(), listens with@Output() - Shared service with a Subject/BehaviorSubject — both components inject the service
- Signals in a shared service —
signal()/computed()for fine-grained reactivity - NgRx / Akita / Elf — for complex global state
// Shared service approach
@Injectable({ providedIn: 'root' })
export class CartService {
private _count = signal(0);
count = this._count.asReadonly();
add() { this._count.update(n => n + 1); }
}
14. What is HttpClient and how do you handle errors?
HttpClient makes HTTP requests and returns Observables. Import provideHttpClient() in app.config.ts.
@Injectable({ providedIn: 'root' })
export class ApiService {
private http = inject(HttpClient);
getUser(id: number): Observable<User> {
return this.http.get<User>(`/api/users/${id}`).pipe(
retry({ count: 2, delay: 1000 }),
catchError(err => {
if (err.status === 404) return throwError(() => new Error('User not found'));
return throwError(() => err);
})
);
}
}
Use HttpInterceptor (or the newer HttpInterceptorFn) to add auth headers, log requests, or handle 401 globally.
RxJS
15. What is an Observable and how does it differ from a Promise?
| Observable | Promise | |
|---|---|---|
| Values | 0, 1, or many | Exactly 1 |
| Execution | Lazy (starts on subscribe) | Eager (starts immediately) |
| Cancellable | Yes (unsubscribe) | No |
| Operators | Rich (map/filter/switchMap…) | Limited (.then/.catch) |
| Sync or async | Both | Always async |
// Promise — eager, one value
const p = fetch('/api/data').then(r => r.json()); // starts now
// Observable — lazy, can emit many
const o$ = this.http.get('/api/data'); // nothing happens yet
const sub = o$.subscribe(data => console.log(data)); // starts now
sub.unsubscribe(); // cancels the request
16. What is the difference between Subject, BehaviorSubject, ReplaySubject, and AsyncSubject?
| Type | Replays to new subscribers | Initial value |
|---|---|---|
Subject |
Nothing (only future values) | None |
BehaviorSubject |
Last value | Required |
ReplaySubject(n) |
Last n values | None |
AsyncSubject |
Last value, only on complete | None |
BehaviorSubject is the most common for state management (new subscribers always get the current value).
17. What is the difference between switchMap, mergeMap, concatMap, and exhaustMap?
All flatten a higher-order Observable (Observable of Observables). They differ in concurrency:
| Operator | Behaviour | Use case |
|---|---|---|
switchMap |
Cancels previous inner Observable when new outer value arrives | Search autocomplete |
mergeMap |
All inner Observables run in parallel | Parallel HTTP requests |
concatMap |
Queues inner Observables, runs sequentially | File upload queue |
exhaustMap |
Ignores new outer values while inner is running | Login button (prevent double submit) |
// Typeahead: cancel previous search on each keystroke
this.searchControl.valueChanges.pipe(
debounceTime(300),
distinctUntilChanged(),
switchMap(term => this.api.search(term))
).subscribe(results => this.results = results);
18. How do you avoid memory leaks with subscriptions?
Options:
// 1. takeUntilDestroyed (Angular 16+, preferred)
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export class MyComponent {
constructor() {
this.service.data$.pipe(takeUntilDestroyed()).subscribe(...);
}
}
// 2. async pipe (auto-unsubscribes in template)
// template: {{ data$ | async }}
// 3. Manual unsubscribe
private destroy$ = new Subject<void>();
ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); }
this.data$.pipe(takeUntil(this.destroy$)).subscribe(...);
Avoid takeUntilDestroyed inside a callback — use it at component init level.
19. What are Angular Signals and when should you use them instead of RxJS?
Signals (Angular 16+, stable in Angular 17) are a reactive primitive for synchronous state. RxJS is better for async event streams.
// Signals
count = signal(0);
doubled = computed(() => this.count() * 2);
increment() { this.count.update(n => n + 1); }
// In template — no async pipe needed
// {{ count() }} {{ doubled() }}
Use signals when: managing component state, deriving values, replacing BehaviorSubject for simple state.
Use RxJS when: HTTP, WebSockets, complex async pipelines, combining multiple event sources.
Interop:
import { toSignal, toObservable } from '@angular/core/rxjs-interop';
// Observable → Signal
data = toSignal(this.http.get<Data>('/api/data'), { initialValue: null });
// Signal → Observable
count$ = toObservable(this.count);
20. What is combineLatest and when would you use it?
combineLatest emits whenever any input Observable emits, combining the latest value from each:
// Show a product with its reviews once both arrive
combineLatest([
this.productService.getProduct(id),
this.reviewService.getReviews(id),
]).subscribe(([product, reviews]) => {
this.product = product;
this.reviews = reviews;
});
Note: combineLatest waits until all sources have emitted at least once. Use forkJoin for one-shot HTTP requests that all complete.
Routing
21. How do you set up lazy loading in Angular?
Lazy loading defers module/component bundles until the route is first visited.
// app.routes.ts
export const routes: Routes = [
{ path: '', component: HomeComponent },
{
path: 'dashboard',
loadComponent: () =>
import('./dashboard/dashboard.component').then(m => m.DashboardComponent),
},
{
path: 'admin',
loadChildren: () =>
import('./admin/admin.routes').then(m => m.adminRoutes),
},
];
loadComponent (standalone) is preferred over loadChildren (module-based) in modern Angular.
22. What are route guards and what are the four types?
Guards control navigation. Angular 14+ supports functional guards (preferred) instead of class-based guards.
| Guard | Interface/Type | Controls |
|---|---|---|
canActivate |
CanActivateFn |
Whether route can be activated |
canActivateChild |
CanActivateChildFn |
Whether child routes can be activated |
canDeactivate |
CanDeactivateFn |
Whether user can leave a route |
canMatch |
CanMatchFn |
Whether route definition can be matched |
resolve |
ResolveFn |
Pre-fetch data before activating |
// Functional auth guard (Angular 15+)
export const authGuard: CanActivateFn = (route, state) => {
const auth = inject(AuthService);
const router = inject(Router);
return auth.isLoggedIn() ? true : router.createUrlTree(['/login']);
};
// Usage
{ path: 'profile', component: ProfileComponent, canActivate: [authGuard] }
23. What is the difference between ActivatedRoute and Router?
ActivatedRoute— provides information about the current route (params, query params, data, URL segments)Router— used for programmatic navigation
export class ProductComponent {
private route = inject(ActivatedRoute);
private router = inject(Router);
constructor() {
// Read route param
this.route.paramMap.subscribe(params => {
this.productId = +params.get('id')!;
});
// Or as a signal (Angular 17+)
// this.productId = this.route.snapshot.paramMap.get('id');
}
goBack() {
this.router.navigate(['/products']);
}
}
24. What is a route resolver and when should you use it?
A resolver pre-fetches data before a route is activated, so the component receives data immediately (no loading state needed in the component).
export const productResolver: ResolveFn<Product> = (route) => {
const id = +route.paramMap.get('id')!;
return inject(ProductService).getProduct(id);
};
// Route config
{ path: 'products/:id', component: ProductDetailComponent, resolve: { product: productResolver } }
// Component
export class ProductDetailComponent {
product = inject(ActivatedRoute).snapshot.data['product'] as Product;
}
When to use: pages where showing an empty/loading skeleton is bad UX (e.g. print views, PDF exports). For most SPAs, handling the loading state in the component is simpler.
25. What is RouterLink vs router.navigate() vs router.navigateByUrl()?
| Use case | |
|---|---|
[routerLink] |
Template-based navigation |
router.navigate(['/path', param]) |
Programmatic navigation (relative, array-based) |
router.navigateByUrl('/path/42') |
Programmatic navigation (absolute URL string) |
router.navigate() is preferred for programmatic navigation because it supports relative paths and query params cleanly.
Forms
26. What is the difference between Template-Driven and Reactive forms?
| Template-Driven | Reactive | |
|---|---|---|
| Setup | FormsModule + ngModel |
ReactiveFormsModule + FormBuilder |
| Control | In template | In class (explicit) |
| Testing | Requires DOM | Unit-testable without DOM |
| Scalability | Simple forms | Complex/dynamic forms |
| Async validators | Harder | First-class |
// Reactive form
export class SignupComponent {
private fb = inject(FormBuilder);
form = this.fb.group({
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required, Validators.minLength(8)]],
});
submit() {
if (this.form.valid) console.log(this.form.value);
}
}
27. How do you write a custom validator?
// Synchronous validator
function noSpaces(control: AbstractControl): ValidationErrors | null {
return control.value?.includes(' ')
? { noSpaces: 'Value must not contain spaces' }
: null;
}
// Async validator (e.g. check username availability)
function uniqueUsername(api: UserApi): AsyncValidatorFn {
return (control: AbstractControl): Observable<ValidationErrors | null> =>
control.value
? api.checkUsername(control.value).pipe(
debounceTime(300),
map(taken => (taken ? { usernameTaken: true } : null)),
catchError(() => of(null))
)
: of(null);
}
// Usage
email: ['', [Validators.required, noSpaces], [uniqueUsername(this.userApi)]]
Performance
28. What is OnPush change detection and when should you use it?
With OnPush, Angular only runs change detection for a component when:
- An
@Input()reference changes (not a mutation) - An event is triggered inside the component tree
- An
asyncpipe in the template emits a new value markForCheck()is called programmatically
Use OnPush for all "dumb" / presentational components that receive data via @Input(). It can dramatically reduce unnecessary DOM checks in large trees.
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
template: `<div>{{ user.name }}</div>`,
})
export class UserCardComponent {
@Input() user!: User; // must pass new object, not mutate existing
}
29. What is trackBy in @for / *ngFor and why does it matter?
Without trackBy, Angular destroys and recreates all DOM nodes when the array changes. trackBy tells Angular to identify items by a key so only changed nodes are updated.
// Old ngFor syntax
*ngFor="let item of items; trackBy: trackById"
trackById(index: number, item: Item) { return item.id; }
// New @for syntax — track is required
@for (item of items; track item.id) {
<app-item [item]="item" />
}
30. What is @defer in Angular 17?
@defer lazy-loads a block of the template (including its components/directives) only when a trigger condition is met, without any route changes.
@defer (on viewport) {
<app-heavy-chart [data]="chartData" />
} @placeholder {
<div class="chart-placeholder"></div>
} @loading (minimum 300ms) {
<app-spinner />
} @error {
<p>Failed to load chart</p>
}
Trigger options: on idle, on viewport, on interaction, on hover, on immediate, on timer(500ms), when condition.
31. How does Angular's SSR (Server-Side Rendering) work?
Angular Universal / Angular SSR renders the app on the server and sends HTML to the browser. The browser hydrates the static HTML, making it interactive.
// app.config.server.ts
export const config: ApplicationConfig = {
providers: [provideServerRendering()],
};
Benefits: better First Contentful Paint (FCP), improved SEO. Gotchas: avoid window/document/localStorage in shared code — check with isPlatformBrowser().
Advanced Concepts
32. What is ContentChild vs ViewChild?
@ViewChild— queries a child element/component in the component's own template@ContentChild— queries content projected via<ng-content>
// ViewChild: references something in THIS template
@ViewChild('myInput') inputRef!: ElementRef;
// ContentChild: references something projected from the parent
@ContentChild(HighlightDirective) highlight?: HighlightDirective;
33. What is ng-content and how does content projection work?
<ng-content> lets a parent pass HTML into a component's template slot.
// card.component.html
<div class="card">
<div class="card-header"><ng-content select="[slot=header]"></ng-content></div>
<div class="card-body"><ng-content></ng-content></div>
</div>
// Parent usage
<app-card>
<h2 slot="header">Title</h2>
<p>Body content here</p>
</app-card>
34. What is HostListener and HostBinding?
@Directive({ selector: '[appHighlight]', standalone: true })
export class HighlightDirective {
@HostBinding('style.background') bg = 'transparent';
@HostBinding('class.active') isActive = false;
@HostListener('click') onClick() {
this.isActive = !this.isActive;
this.bg = this.isActive ? 'yellow' : 'transparent';
}
}
In Angular 17+, prefer host property in the decorator:
@Directive({
selector: '[appHighlight]',
host: {
'(click)': 'onClick()',
'[style.background]': 'bg',
}
})
35. What is forwardRef and when do you need it?
forwardRef defers the reference to a class that hasn't been declared yet (used to fix circular dependency issues in DI or NG_VALUE_ACCESSOR):
// Custom form control implementing ControlValueAccessor
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CustomInputComponent),
multi: true,
}]
36. What is the Angular CDK and what does it provide?
The Component Dev Kit (CDK) provides primitives for building custom UI components:
- Drag and drop (
DragDropModule) - Overlay / portal for dynamic content
- Accessibility (
FocusTrap,A11yModule,LiveAnnouncer) - Scrolling (virtual scroll for large lists)
- Table (
CdkTable, basis forMatTable) - Text field auto-resize
Import from @angular/cdk/* separately from Angular Material.
Testing
37. What is TestBed and how do you set up a component test?
TestBed is Angular's testing environment. It configures and creates an Angular testing module.
describe('UserCardComponent', () => {
let component: UserCardComponent;
let fixture: ComponentFixture<UserCardComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [UserCardComponent], // standalone component
}).compileComponents();
fixture = TestBed.createComponent(UserCardComponent);
component = fixture.componentInstance;
component.user = { id: 1, name: 'Alice' };
fixture.detectChanges();
});
it('displays the user name', () => {
const el = fixture.nativeElement.querySelector('h2');
expect(el.textContent).toContain('Alice');
});
});
38. How do you mock a service in an Angular test?
// Option 1: provide a spy object
const authSpy = jasmine.createSpyObj('AuthService', ['isLoggedIn']);
authSpy.isLoggedIn.and.returnValue(true);
await TestBed.configureTestingModule({
imports: [AppComponent],
providers: [{ provide: AuthService, useValue: authSpy }],
}).compileComponents();
// Option 2: override provider after setup
TestBed.overrideProvider(AuthService, { useValue: authSpy });
39. What is fixture.detectChanges() and when do you call it?
detectChanges() triggers Angular's change detection cycle in the test, updating the DOM to reflect the current component state. Call it:
- Once after
createComponentto run initial lifecycle hooks (ngOnInit) - Again after changing
@Input()values or component properties - After async operations complete (use
fakeAsync+tick()orwaitForAsync)
it('shows loading spinner while fetching', fakeAsync(() => {
component.loading = true;
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('app-spinner')).toBeTruthy();
component.loading = false;
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('app-spinner')).toBeNull();
}));
40. What is the async pipe and why is it preferred in templates?
The async pipe:
- Subscribes to an Observable or Promise
- Returns the latest value
- Auto-unsubscribes when the component is destroyed
<!-- Without async pipe — manual subscription, risk of memory leak -->
<div>{{ username }}</div>
<!-- With async pipe — no subscription management needed -->
<div>{{ username$ | async }}</div>
<!-- With null guard -->
@if (user$ | async; as user) {
<div>{{ user.name }}</div>
}
Signals (Angular 16–17+)
41. What are the three core Signal primitives?
| Primitive | Description |
|---|---|
signal(value) |
Writable reactive value |
computed(() => expr) |
Derived value, recalculated lazily when deps change |
effect(() => sideEffect) |
Runs when signals it reads change |
export class CounterComponent {
count = signal(0);
doubled = computed(() => this.count() * 2);
constructor() {
effect(() => console.log('Count changed to', this.count()));
}
increment() { this.count.update(n => n + 1); }
reset() { this.count.set(0); }
}
42. What are input(), output(), and model() (Angular 17.1+)?
Angular 17.1 introduced signal-based component I/O to replace @Input(), @Output(), and [(ngModel)]:
export class RatingComponent {
// Signal input (required variant)
value = input.required<number>();
// Optional input with default
max = input(5);
// Two-way binding (replaces @Input + @Output)
rating = model(0);
// Output event
selected = output<number>();
select(n: number) {
this.rating.set(n);
this.selected.emit(n);
}
}
<!-- Parent -->
<app-rating [(rating)]="userRating" [max]="10" (selected)="onSelect($event)" />
Angular CLI & Build
43. What are the most important Angular CLI commands?
| Command | Purpose |
|---|---|
ng new my-app |
Create new project |
ng generate component foo |
Generate component (ng g c) |
ng generate service auth |
Generate service (ng g s) |
ng serve |
Dev server (HMR enabled) |
ng build |
Production build |
ng test |
Run unit tests (Karma/Jest) |
ng e2e |
Run E2E tests |
ng lint |
Run linter |
ng update |
Update Angular and dependencies |
ng add @angular/material |
Add Angular Material |
44. What is the difference between ng build and ng build --configuration production?
ng build (dev) — no minification, with source maps.ng build --configuration production — enables:
- Tree shaking (removes unused code)
- Minification and uglification
- AOT compilation (Ahead-of-Time, default since Angular 9)
- Bundle optimisation and code splitting
45. What is AOT (Ahead-of-Time) compilation?
AOT compiles Angular templates at build time instead of at runtime (JIT). Benefits:
- Smaller bundles (compiler not shipped to browser)
- Faster rendering (template already compiled)
- Earlier template error detection
- Better security (no
eval()in the browser)
AOT is the default since Angular 9 and is always used in production builds.
Common Mistakes
| Mistake | Problem | Fix |
|---|---|---|
Mutating @Input() objects |
Parent state silently changes | Emit new object via @Output() |
| Subscribing without unsubscribing | Memory leaks | Use async pipe, takeUntilDestroyed() |
Using any in templates |
Loses type safety | Type all inputs explicitly |
| Calling a function in template | Runs on every CD cycle | Use computed() signal or pure pipe |
Forgetting trackBy / track in lists |
Full DOM re-creation | Always add track item.id |
*ngIf + *ngFor on same element |
Template error | Wrap with <ng-container> |
subscribe inside subscribe |
Pyramid of doom | Use switchMap / combineLatest |
Using document directly |
Breaks SSR | Use DOCUMENT token or Renderer2 |
Angular vs React vs Vue
| Angular | React | Vue | |
|---|---|---|---|
| Type | Full framework | UI library | Progressive framework |
| Language | TypeScript | JS/TS | JS/TS |
| Reactivity | Signals + RxJS | useState/useEffect | Signals (Vapor) + Composition API |
| DI | Built-in hierarchical | Manual / context | Manual / provide-inject |
| Forms | Template + Reactive | Uncontrolled/libraries | v-model |
| CLI | ng (powerful) |
create-react-app/Vite |
create-vue |
| Bundle size (core) | Large (~50 kB) | Medium (~35 kB) | Small (~22 kB) |
| Learning curve | Steep | Medium | Gentle |
FAQ
Q: Should I use NgModules or standalone components in 2024?
A: Standalone components are the default in Angular 17 and the recommended approach for all new projects. NgModules are still supported but considered legacy for new code.
Q: When should I use Signals vs RxJS?
A: Signals for synchronous component state and derived values. RxJS for async streams, HTTP, WebSockets, or complex multi-source event pipelines. Use toSignal() / toObservable() to bridge them.
Q: What is the difference between ngOnInit and the constructor?
A: The constructor runs first and should only set up DI (inject services). @Input() values are not yet available in the constructor. Use ngOnInit for initialisation logic that depends on inputs or that triggers side effects.
Q: How does Angular handle forms with deeply nested groups?
A: Use FormGroup nesting with FormBuilder.group(). Access nested controls with form.get('address.street') or form.get(['address', 'street']). Use FormArray for dynamic lists of controls.
Q: What is the difference between canActivate and canMatch?
A: canActivate blocks navigation to a matched route. canMatch controls whether the route definition is even considered during matching — useful when you have multiple routes at the same path that serve different user roles.
Q: How do you upgrade Angular to a new major version?
A: Run ng update @angular/core @angular/cli which applies schematics to automatically migrate breaking changes. Check the Angular Update Guide first — it lists all breaking changes and required manual steps for each version pair.