Angular Interview Questions (2025)
How to use: Click any question to expand the answer. Questions are ordered from Easy → Medium → Hard.
Easy (Q1–Q30)
Section titled “Easy (Q1–Q30)”Q1. What is Angular? Easy
Angular is an open-source, TypeScript-based front-end web application framework maintained by Google. It is used to build single-page applications (SPAs) that are fast, scalable, and maintainable.
Angular provides a complete solution out of the box: component-based architecture, routing (@angular/router), HTTP client (@angular/common/http), reactive forms, dependency injection, and a powerful CLI.
Q2. What is the difference between AngularJS and Angular? Easy
| AngularJS (1.x) | Angular (2+) | |
|---|---|---|
| Language | JavaScript | TypeScript |
| Architecture | MVC with $scope | Component-based |
| Data binding | Two-way via $scope | One-way by default, two-way with [()] |
| Mobile | Poor | Good |
| Performance | Slower | Much faster (Ivy renderer) |
| CLI | None | @angular/cli |
They are not backward compatible — completely different frameworks.
Q3. What is a Component in Angular? Easy
A Component is the basic building block of an Angular application. It controls a specific piece of the UI.
Every component has three parts:
- TypeScript class — logic and data
- HTML template — the view
- CSS styles — look and feel
Decorated with @Component({ selector, templateUrl, styleUrls }).
@Component({ selector: 'app-hello', template: `<h1>Hello {{ name }}</h1>`,})export class HelloComponent { name = 'Angular';}Q4. What is a Module in Angular? Easy
A Module (@NgModule) is a container that groups related components, directives, pipes, and services. Every Angular app has at least one module — AppModule.
@NgModule takes:
declarations— components/directives/pipes owned by this moduleimports— other modules to useproviders— services to registerexports— what other modules can usebootstrap— root component (AppModule only)
In modern Angular (v14+), standalone components reduce the need for NgModules.
Q5. What are the four types of data binding in Angular? Easy
- Interpolation
{{ expression }}— Component → Template (renders as string) - Property Binding
[property]="value"— Component → Template (binds any type) - Event Binding
(event)="handler()"— Template → Component (user events) - Two-Way Binding
[(ngModel)]="prop"— Both directions simultaneously
<p>{{ title }}</p> <!-- Interpolation --><img [src]="imageUrl" /> <!-- Property binding --><button (click)="save()">Save</button> <!-- Event binding --><input [(ngModel)]="name" /> <!-- Two-way binding -->Q6. What is `*ngIf` and how does it work? Easy
*ngIf is a structural directive that conditionally adds or removes an element from the DOM.
Unlike display: none, *ngIf completely destroys the element when false, freeing memory. When truthy again, the element is recreated from scratch.
<div *ngIf="isLoggedIn; else loginTpl">Welcome!</div><ng-template #loginTpl><a routerLink="/login">Login</a></ng-template>Use display: none when you need to preserve state (like form inputs); use *ngIf when the branch is truly not needed.
Q7. What is `*ngFor` and what is `trackBy`? Easy
*ngFor repeats a template for each item in a collection.
Available local variables: index, first, last, even, odd.
trackBy is a performance optimization — it gives Angular a unique key per item so Angular only re-renders items that actually changed, instead of re-rendering the whole list.
<li *ngFor="let user of users; trackBy: trackById">{{ user.name }}</li>trackById(index: number, user: User) { return user.id; }Q8. What are the three types of Directives? Easy
- Component Directives — Components are directives with a template (
@Component) - Structural Directives — Change DOM structure by adding/removing elements. Prefixed with
*:*ngIf,*ngFor,*ngSwitch - Attribute Directives — Change appearance/behavior of an existing element without altering DOM structure:
ngClass,ngStyle,ngModel
Q9. What is a Pipe in Angular? Easy
A Pipe transforms data for display in templates using the | character.
{{ price | currency:'INR' }}{{ today | date:'dd/MM/yyyy' }}{{ name | uppercase }}Built-in pipes: date, uppercase, lowercase, currency, number, percent, json, slice, async, keyvalue.
Custom pipes implement PipeTransform:
@Pipe({ name: 'truncate' })export class TruncatePipe implements PipeTransform { transform(value: string, limit = 30): string { return value.length > limit ? value.slice(0, limit) + '…' : value; }}Q10. What is a Service in Angular? Easy
A Service is a class decorated with @Injectable() that holds shared logic or data — API calls, business logic, state management.
@Injectable({ providedIn: 'root' })export class UserService { constructor(private http: HttpClient) {} getUsers() { return this.http.get<User[]>('/api/users'); }}providedIn: 'root' registers it as a singleton available app-wide without adding it to any module’s providers.
Q11. What is Dependency Injection (DI) in Angular? Easy
Dependency Injection is a design pattern where a class receives its dependencies from an external source rather than creating them itself. Angular’s DI system:
- You declare what you need in the constructor:
constructor(private svc: UserService) - Angular’s injector creates/provides the dependency for you
Benefits: loose coupling, easier testing (can inject mocks), reusability.
export class ProfileComponent { constructor(private userSvc: UserService) {} // DI provides UserService}Q12. What is Angular CLI? Name common commands. Easy
Angular CLI is a command-line tool for scaffolding, building, and managing Angular projects.
Common commands:
ng new my-app # create new projectng serve # dev server (localhost:4200)ng generate component foo # create componentng generate service bar # create serviceng build --prod # production buildng test # run unit testsng lint # lint codeQ13. What is `@Input()` and `@Output()`? Easy
@Input() — Allows parent to pass data into a child component:
// child@Input() title: string = '';<!-- parent --><app-card [title]="'Hello'"></app-card>@Output() — Allows child to emit events up to parent using EventEmitter:
// child@Output() saved = new EventEmitter<string>();onSave() { this.saved.emit('data'); }<!-- parent --><app-form (saved)="onSaved($event)"></app-form>Q14. What is `ngOnInit`? Why use it instead of the constructor? Easy
ngOnInit is a lifecycle hook called once after the component’s input properties are set for the first time.
Use ngOnInit for initialization logic (fetching data, setting up subscriptions) because:
- Constructor runs before
@Input()values are available - Constructor should only do DI (declare dependencies)
ngOnInitis where the component is ready to use
ngOnInit() { this.userSvc.getUsers().subscribe(users => this.users = users);}Q15. What is the Angular component lifecycle order? Easy
Order of lifecycle hooks:
constructorngOnChanges(called before init and on every @Input change)ngOnInit(once, after first ngOnChanges)ngDoCheck(every change detection cycle)ngAfterContentInit(once, after content projection)ngAfterContentCheckedngAfterViewInit(once, after view + children init)ngAfterViewCheckedngOnDestroy(just before component destroyed)
Most commonly used: ngOnInit, ngOnChanges, ngOnDestroy.
Q16. What is `ngOnDestroy` used for? Easy
ngOnDestroy runs just before Angular destroys the component. Use it to clean up resources to prevent memory leaks:
private sub = new Subscription();
ngOnInit() { this.sub = this.svc.data$.subscribe(d => this.data = d);}
ngOnDestroy() { this.sub.unsubscribe(); // prevent memory leak}Other cleanup: clear intervals/timeouts, detach event listeners, complete subjects.
Q17. What is `ViewChild` and `ContentChild`? Easy
@ViewChild — access a child component/element in the component’s own template:
@ViewChild('myInput') inputRef!: ElementRef;ngAfterViewInit() { this.inputRef.nativeElement.focus(); }@ContentChild — access a child projected via <ng-content> from the parent:
@ContentChild(SomeComponent) projected!: SomeComponent;Key rule: @ViewChild is available in ngAfterViewInit; @ContentChild in ngAfterContentInit.
Q18. What is RouterLink and how does it differ from `href`? Easy
routerLink is Angular’s directive for navigation that keeps it a single-page app — no full page reload:
<a routerLink="/about">About</a><a [routerLink]="['/user', userId]">Profile</a>Using href="/about" causes a full page reload, losing all app state. routerLink uses the HTML5 History API to change the URL and let Angular’s router handle rendering.
routerLinkActive="active" adds a CSS class when the route matches.
Q19. What is `ngModel` and which module provides it? Easy
ngModel implements two-way data binding on form controls. It requires FormsModule (or ReactiveFormsModule).
<input [(ngModel)]="username" /><p>Hello, {{ username }}</p>[(ngModel)] is syntactic sugar for [ngModel]="username" (ngModelChange)="username = $event".
In standalone components, import FormsModule directly in the component’s imports array.
Q20. What is `ngClass` and `ngStyle`? Easy
ngClass — dynamically add/remove CSS classes:
<div [ngClass]="{ active: isActive, error: hasError }"></div><div [ngClass]="['btn', isPrimary ? 'btn-primary' : 'btn-secondary']"></div>ngStyle — dynamically set inline styles:
<div [ngStyle]="{ color: textColor, 'font-size': fontSize + 'px' }"></div>Prefer [class.active]="isActive" and [style.color]="color" syntax for single class/style bindings — cleaner.
Q21. What is the `async` pipe? Easy
The async pipe automatically subscribes to an Observable or Promise and returns the latest value. It also unsubscribes automatically on component destroy — no manual cleanup needed.
<div *ngIf="users$ | async as users"> <li *ngFor="let u of users">{{ u.name }}</li></div>This is the preferred pattern over manually subscribing in ngOnInit, because it avoids memory leaks and works with OnPush change detection.
Q22. What is `ng-content`? Easy
<ng-content> enables content projection — inserting content from a parent into a child’s template (similar to slot in web components).
<div class="card"> <ng-content select="[slot=header]"></ng-content> <ng-content></ng-content></div><!-- parent usage --><app-card> <h2 slot="header">Title</h2> <p>Body content here</p></app-card>Q23. What is `ng-template` and `ng-container`? Easy
<ng-template> — defines a template block that is NOT rendered unless explicitly used (via *ngIf else, ngTemplateOutlet, etc.):
<div *ngIf="loggedIn; else guestTpl">Welcome back!</div><ng-template #guestTpl><a href="/login">Login</a></ng-template><ng-container> — a logical grouping element that renders no DOM element itself. Useful to apply structural directives without adding a wrapper <div>:
<ng-container *ngIf="items.length"> <li *ngFor="let i of items">{{ i }}</li></ng-container>Q24. What is Angular Router and how do you define routes? Easy
Angular Router maps URL paths to components.
const routes: Routes = [ { path: '', component: HomeComponent }, { path: 'about', component: AboutComponent }, { path: 'user/:id', component: UserComponent }, { path: '**', component: NotFoundComponent }, // wildcard];Add <router-outlet></router-outlet> in app.component.html to mark where routed components render.
In standalone apps: provideRouter(routes) in app.config.ts.
Q25. What is `HttpClient` and how do you use it? Easy
HttpClient (from @angular/common/http) is Angular’s service for making HTTP requests. It returns Observables.
// app.config.ts (standalone)provideHttpClient()
// serviceconstructor(private http: HttpClient) {}
getUsers() { return this.http.get<User[]>('https://api.example.com/users');}
createUser(user: User) { return this.http.post('/api/users', user);}Always subscribe (or use async pipe) to trigger the request — Observables are lazy.
Q26. What is `FormModule` vs `ReactiveFormsModule`? Easy
Template-driven forms (FormsModule):
- Logic in the template (
ngModel) - Less code, easier for simple forms
- Asynchronous, not easily unit-testable
Reactive forms (ReactiveFormsModule):
- Logic in the TypeScript class (
FormGroup,FormControl) - More verbose but more powerful and testable
- Synchronous, observable-based, preferred for complex forms
// Reactive examplethis.form = this.fb.group({ email: ['', [Validators.required, Validators.email]], password: ['', Validators.minLength(8)],});Q27. What is `EventEmitter`? Easy
EventEmitter is a class used with @Output() to emit custom events from child to parent. It extends RxJS Subject.
@Output() submitted = new EventEmitter<FormData>();
onSubmit() { this.submitted.emit(this.formData);}<app-form (submitted)="handleSubmit($event)"></app-form>Use EventEmitter only for @Output() events; for other pub/sub needs, use a plain RxJS Subject.
Q28. What does `providedIn: 'root'` mean? Easy
providedIn: 'root' in @Injectable registers the service as a singleton at the application root injector level. The service is available everywhere in the app without adding it to any module’s providers array.
@Injectable({ providedIn: 'root' })export class AuthService {}Benefits: tree-shakable (not bundled if unused), automatic singleton, no module boilerplate. Alternatives: providedIn: 'platform', providedIn: 'any', or module-level providers.
Q29. What is ``? Easy
<router-outlet> is a placeholder directive that Angular’s router uses to render the matched route’s component. Think of it as the “mount point” for routed views.
<app-navbar></app-navbar><router-outlet></router-outlet> <!-- routed component renders here --><app-footer></app-footer>Named outlets allow multiple router outlets: <router-outlet name="sidebar"></router-outlet>.
Q30. What is `ngOnChanges`? Easy
ngOnChanges fires before ngOnInit and every time a data-bound @Input() property value changes. It receives a SimpleChanges object with previous and current values.
ngOnChanges(changes: SimpleChanges) { if (changes['userId']) { const prev = changes['userId'].previousValue; const curr = changes['userId'].currentValue; this.loadUser(curr); }}Only fires for @Input() changes, not for internal property mutations.
Medium (Q31–Q70)
Section titled “Medium (Q31–Q70)”Q31. What is an Observable in Angular? How does it differ from a Promise? Medium
| Observable | Promise | |
|---|---|---|
| Values | Multiple (stream) | Single |
| Execution | Lazy (only runs on subscribe) | Eager (runs immediately) |
| Cancellable | Yes (unsubscribe()) | No |
| Operators | Rich RxJS operators | .then()/.catch() only |
| Async/await | Needs firstValueFrom() | Native support |
// Observable - lazy, multiple valuesconst obs$ = interval(1000);const sub = obs$.subscribe(n => console.log(n));sub.unsubscribe(); // cancel
// Promise - eager, one valueconst p = fetch('/api').then(r => r.json());Observables are preferred in Angular for HTTP, routing events, and form value changes.
Q32. What is RxJS? Name common operators used in Angular. Medium
RxJS (Reactive Extensions for JavaScript) is a library for composing asynchronous programs using observable sequences.
Common Angular operators:
map— transform each valuefilter— filter valuesswitchMap— cancel previous inner observable, switch to new (HTTP search)mergeMap— run inner observables in parallelconcatMap— run inner observables sequentiallytap— side effects without transformingcatchError— error handlingdebounceTime— wait N ms before emitting (search input)distinctUntilChanged— skip duplicate consecutive valuestakeUntil— complete when another observable emits (unsubscribe pattern)forkJoin— wait for all observables to complete (parallel HTTP)combineLatest— emit when any source emits, with all latest values
Q33. What is a Subject? Types of Subjects in RxJS? Medium
A Subject is both an Observable and an Observer — it can emit values AND be subscribed to. It’s multicasting (all subscribers get the same emission).
const subject = new Subject<string>();subject.subscribe(v => console.log('A:', v));subject.subscribe(v => console.log('B:', v));subject.next('hello'); // both A and B receive 'hello'Types:
Subject— no initial value, only future emissionsBehaviorSubject— holds a current value, new subscribers get it immediatelyReplaySubject(n)— replays last N values to new subscribersAsyncSubject— emits only the last value when the sequence completes
BehaviorSubject is the most common for shared state in services.
Q34. What is lazy loading in Angular routing? Medium
Lazy loading loads feature modules only when the user navigates to their route — reducing initial bundle size and improving startup time.
const routes: Routes = [ { path: 'admin', loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule) }, // Standalone (Angular 14+) { path: 'dashboard', loadComponent: () => import('./dashboard.component').then(c => c.DashboardComponent) },];The router code-splits the module into a separate chunk. Angular CLI automatically splits at every loadChildren/loadComponent boundary.
Q35. What are Route Guards? Name the types. Medium
Route Guards control whether navigation can proceed or not.
Types (functional guards, Angular 15+):
| Guard | Purpose |
|---|---|
canActivate | Can user enter this route? |
canActivateChild | Can user enter child routes? |
canDeactivate | Can user leave this route? (unsaved changes) |
canMatch | Should this route even be matched? |
resolve | Pre-fetch data before route activates |
// Functional guard (modern)export const authGuard: CanActivateFn = (route, state) => { const auth = inject(AuthService); return auth.isLoggedIn() ? true : inject(Router).createUrlTree(['/login']);};Q36. What is a Resolver in Angular? Medium
A Resolver pre-fetches data before a route activates. The router waits for the resolver to complete before rendering the component — so the component always has data immediately.
export const userResolver: ResolveFn<User> = (route) => { const id = route.paramMap.get('id')!; return inject(UserService).getUser(id);};
// Route definition{ path: 'user/:id', component: UserComponent, resolve: { user: userResolver } }
// In componentconstructor(private route: ActivatedRoute) {}ngOnInit() { this.user = this.route.snapshot.data['user'];}Q37. What is `ChangeDetectionStrategy.OnPush`? Medium
By default, Angular checks the entire component tree on every event. OnPush tells Angular to only check a component when:
- An
@Input()reference changes - An event originates from the component or its children
- An Observable subscribed via
asyncpipe emits - Change detection is manually triggered (
MarkForCheck,detectChanges)
@Component({ selector: 'app-list', changeDetection: ChangeDetectionStrategy.OnPush, // ...})Key rule: With OnPush, mutating an object/array won’t trigger updates — you must pass a new reference ([...arr] or {...obj}).
Performance optimization for large component trees.
Q38. What is Zone.js and NgZone? Medium
Zone.js is a library that monkey-patches browser async APIs (setTimeout, Promises, XHR, etc.) to intercept their execution. Angular uses it to know when async operations complete so it can trigger change detection automatically.
NgZone is Angular’s wrapper around Zone.js with two key methods:
ngZone.run()— run code inside Angular’s zone (triggers change detection)ngZone.runOutsideAngular()— run code outside Angular’s zone (performance, e.g., heavy animations)
constructor(private ngZone: NgZone) {}
startHeavyWork() { this.ngZone.runOutsideAngular(() => { // runs without triggering CD on every frame requestAnimationFrame(() => this.ngZone.run(() => this.done = true)); });}In Angular 18+, zoneless mode is experimental — apps can opt out of Zone.js entirely using Signals.
Q39. What is the difference between pure and impure pipes? Medium
Pure pipe (default) — only re-runs transform() when the input reference changes. Angular can cache the result. Suitable for most transformations.
Impure pipe — re-runs transform() on every change detection cycle, regardless of whether the input changed. Use only when the pipe depends on mutable state (e.g., current date, external state).
@Pipe({ name: 'myPipe', pure: false }) // impureexport class MyPipe implements PipeTransform { ... }Impure pipes can hurt performance. Avoid unless necessary — prefer storing derived values in the component instead.
Q40. What is `ViewEncapsulation`? What are the modes? Medium
ViewEncapsulation controls how component styles are scoped.
| Mode | Behavior |
|---|---|
Emulated (default) | Angular adds unique attribute selectors to CSS — styles don’t leak out or in |
ShadowDom | Uses native Shadow DOM — true isolation |
None | No encapsulation — styles are global |
@Component({ encapsulation: ViewEncapsulation.None, // styles go global})Emulated is best for most cases. Use None when you need to style third-party components. ShadowDom when building web components.
Q41. What is `FormGroup`, `FormControl`, and `FormBuilder`? Medium
FormControl — tracks value and validity of a single input field.
FormGroup — groups multiple FormControls into a single object.
FormArray — dynamic list of controls.
FormBuilder — service that simplifies creating form groups.
constructor(private fb: FormBuilder) {}
this.form = this.fb.group({ name: ['', Validators.required], email: ['', [Validators.required, Validators.email]], address: this.fb.group({ city: [''], zip: [''] })});
// Access valuesthis.form.get('email')?.value;this.form.value; // all values as objectthis.form.valid; // booleanQ42. How do you create a custom validator in Reactive Forms? Medium
A custom validator is a function that returns null (valid) or a ValidationErrors object (invalid).
// Sync validatorfunction noSpaces(control: AbstractControl): ValidationErrors | null { return control.value?.includes(' ') ? { noSpaces: 'No spaces allowed' } : null;}
// Use itthis.form = this.fb.group({ username: ['', [Validators.required, noSpaces]]});Cross-field validator (on the FormGroup):
function passwordMatch(group: AbstractControl): ValidationErrors | null { const p = group.get('password')?.value; const c = group.get('confirm')?.value; return p === c ? null : { mismatch: true };}Q43. What is an HTTP Interceptor? Medium
An HTTP Interceptor intercepts every HTTP request/response to add cross-cutting behavior: auth tokens, logging, error handling, loading spinners.
// Functional interceptor (Angular 15+)export const authInterceptor: HttpInterceptorFn = (req, next) => { const token = inject(AuthService).getToken(); const authReq = req.clone({ headers: req.headers.set('Authorization', `Bearer ${token}`) }); return next(authReq).pipe( catchError(err => { if (err.status === 401) inject(Router).navigate(['/login']); return throwError(() => err); }) );};
// Register in app.config.tsprovideHttpClient(withInterceptors([authInterceptor]))Q44. What is `switchMap` and when to use it? Medium
switchMap maps each source emission to an inner Observable, cancelling the previous inner observable when a new one arrives.
Best use case: Search-as-you-type (cancel previous HTTP call when user types again).
this.searchControl.valueChanges.pipe( debounceTime(300), distinctUntilChanged(), switchMap(query => this.apiService.search(query))).subscribe(results => this.results = results);- Use
switchMapwhen only the latest result matters (search, route params) - Use
mergeMapwhen all results matter (parallel, order doesn’t matter) - Use
concatMapwhen order matters (sequential queue) - Use
exhaustMapwhen you want to ignore new emissions while processing (login button)
Q45. What is `HostListener` and `HostBinding`? Medium
Used in directives/components to interact with the host element (the element the directive is applied to).
@HostListener — listens to events on the host element:
@Directive({ selector: '[appHighlight]' })export class HighlightDirective { @HostListener('mouseenter') onEnter() { this.el.nativeElement.style.background = 'yellow'; } @HostListener('mouseleave') onLeave() { this.el.nativeElement.style.background = ''; } constructor(private el: ElementRef) {}}@HostBinding — binds to a property of the host element:
@HostBinding('class.active') isActive = false;@HostBinding('attr.disabled') isDisabled = false;Q46. What is `ElementRef` and `Renderer2`? When to use Renderer2 over ElementRef? Medium
ElementRef — gives direct access to the native DOM element:
constructor(private el: ElementRef) {}ngAfterViewInit() { this.el.nativeElement.style.color = 'red'; // direct DOM mutation}Renderer2 — Angular’s abstraction for DOM manipulation. Safer:
constructor(private renderer: Renderer2, private el: ElementRef) {}ngAfterViewInit() { this.renderer.setStyle(this.el.nativeElement, 'color', 'red');}Use Renderer2 because:
- Works with Angular Universal (SSR) — no
document/windowdependency - Works in Web Workers
- XSS-safe for setting attributes
- Future-proof if Angular moves off the DOM
Avoid nativeElement direct manipulation unless absolutely necessary.
Q47. What is `forRoot()` and `forChild()` pattern? Medium
Pattern used by Angular modules (like RouterModule, StoreModule) to configure a module with providers at the root vs feature level.
forRoot(config)— call once inAppModule. Registers the service as a singleton + module declarations. E.g.,RouterModule.forRoot(routes).forChild()— call in feature modules. Registers only declarations (no duplicate singleton providers). E.g.,RouterModule.forChild(featureRoutes).
This prevents multiple instances of services (like the router) when lazy-loaded modules are included.
// AppModuleRouterModule.forRoot(appRoutes)
// AdminModule (lazy loaded)RouterModule.forChild(adminRoutes)Q48. What is `TemplateRef` and `ViewContainerRef`? Medium
TemplateRef — represents an embedded template (<ng-template>). It is a blueprint — not rendered until inserted.
ViewContainerRef — a container in the DOM where you can dynamically insert views (from TemplateRef or components).
@Directive({ selector: '[appIf]' })export class IfDirective { constructor( private tpl: TemplateRef<any>, private vc: ViewContainerRef ) {}
@Input() set appIf(condition: boolean) { if (condition) this.vc.createEmbeddedView(this.tpl); else this.vc.clear(); }}This is how *ngIf is implemented internally.
Q49. What is `SharedModule` and `CoreModule` pattern? Medium
An architectural pattern for organizing modules:
CoreModule — imported once in AppModule. Contains:
- Singleton services (guards, interceptors)
- Root-level components (Navbar, Footer)
- Guards constructor to throw if imported more than once
SharedModule — imported in every feature module that needs it. Contains:
- Reusable components, directives, pipes
CommonModule,ReactiveFormsModulere-exports- No services (would break singleton pattern)
@NgModule({ declarations: [LoadingSpinnerComponent, TruncatePipe], imports: [CommonModule], exports: [LoadingSpinnerComponent, TruncatePipe, CommonModule]})export class SharedModule {}In modern standalone Angular, this pattern is less needed — import directly.
Q50. What is the `async`/`await` equivalent for Observables? Medium
Use firstValueFrom() or lastValueFrom() from RxJS to convert an Observable to a Promise so you can use async/await:
import { firstValueFrom } from 'rxjs';
async loadUser(id: string) { try { const user = await firstValueFrom(this.userService.getUser(id)); this.user = user; } catch (err) { console.error(err); }}firstValueFrom takes the first emission and completes. lastValueFrom waits for completion and takes the last value. The old toPromise() is deprecated.
Q51. What is `InjectionToken`? Medium
InjectionToken creates a DI token for non-class values (strings, objects, configs, functions) that can’t be used as DI tokens directly.
// Create tokenexport const API_URL = new InjectionToken<string>('API_URL');
// Provide in app.config.ts{ provide: API_URL, useValue: 'https://api.example.com' }
// Inject in serviceconstructor(@Inject(API_URL) private apiUrl: string) {}In modern Angular, use inject() function:
const apiUrl = inject(API_URL);Q52. What is hierarchical DI in Angular? Medium
Angular has a tree of injectors that mirrors the component tree. Each node can have its own providers.
When resolving a dependency:
- Check the component’s own injector
- Walk up the component tree
- Check the module injector
- Check the root (environment) injector
This means you can have multiple instances of a service — useful for state scoped to a subtree:
@Component({ providers: [CounterService], // local instance for this component and its children})export class WidgetComponent {}Two widgets → two independent CounterService instances.
Q53. What is AOT compilation? Difference from JIT? Medium
JIT (Just-in-Time): Templates compiled in the browser at runtime. Larger bundle (includes compiler). Slower first load. Good for development.
AOT (Ahead-of-Time): Templates compiled at build time on the server. No compiler shipped. Faster rendering. Detects template errors at build time. Default in production builds.
ng build # AOT by defaultng serve --aot # AOT in devAOT benefits:
- Smaller bundle (no Angular compiler)
- Faster render (no compilation step)
- Earlier error detection
- More secure (template injections caught at build time)
Q54. How do you handle errors in Angular HTTP requests? Medium
this.http.get('/api/data').pipe( catchError((error: HttpErrorResponse) => { if (error.status === 0) { // Network error console.error('Network error:', error.error); } else { // Server error console.error(`Server error ${error.status}:`, error.message); } return throwError(() => new Error('Something went wrong')); })).subscribe({ next: data => this.data = data, error: err => this.errorMsg = err.message});For global error handling, use an HTTP interceptor with catchError.
For uncaught errors, implement ErrorHandler:
@Injectable()class GlobalErrorHandler implements ErrorHandler { handleError(error: any) { /* log to Sentry etc */ }}Q55. What is `forkJoin`? When do you use it? Medium
forkJoin runs multiple observables in parallel and emits an array of their last values only when all complete. Perfect for parallel HTTP calls.
forkJoin({ user: this.http.get<User>('/api/user/1'), orders: this.http.get<Order[]>('/api/orders'), config: this.http.get<Config>('/api/config'),}).subscribe(({ user, orders, config }) => { this.user = user; this.orders = orders;});Caveat: If any observable errors, forkJoin errors and others are cancelled. Handle with catchError on each inner observable if needed.
combineLatest emits on every emission. forkJoin only emits once when all complete.
Q56. What is `takeUntil` pattern for unsubscription? Medium
takeUntil completes a subscription when another observable emits — commonly used for automatic unsubscription on component destroy.
private destroy$ = new Subject<void>();
ngOnInit() { this.service.data$.pipe( takeUntil(this.destroy$) ).subscribe(d => this.data = d);
interval(1000).pipe( takeUntil(this.destroy$) ).subscribe(n => this.counter = n);}
ngOnDestroy() { this.destroy$.next(); this.destroy$.complete();}Cleaner alternative in Angular 16+: takeUntilDestroyed() from @angular/core/rxjs-interop:
data$ = this.service.data$.pipe(takeUntilDestroyed());Q57. What is Angular Universal (SSR)? Medium
Angular Universal enables Server-Side Rendering (SSR) — the Angular app runs on Node.js server to render HTML, which is sent to the browser. This gives:
- Better SEO — crawlers see content immediately
- Faster First Contentful Paint — user sees content before JS loads
- Social media previews —
<meta>tags resolved at render time
ng add @angular/ssrng build && node dist/server/server.mjsPitfalls:
- No
window,document,localStorageon the server — guard withisPlatformBrowser() - State transfer (
TransferState) prevents double HTTP requests
In Angular 17+, SSR is a first-class option available in ng new.
Q58. What is `ActivatedRoute`? How do you read route params? Medium
ActivatedRoute gives you information about the currently active route — params, query params, data, URL.
constructor(private route: ActivatedRoute) {}
ngOnInit() { // Snapshot (one-time read) const id = this.route.snapshot.paramMap.get('id');
// Observable (reacts to param changes without recreating component) this.route.paramMap.subscribe(params => { const id = params.get('id'); this.loadUser(id); });
// Query params this.route.queryParams.subscribe(qp => this.page = qp['page']);}Use the Observable approach when navigating between the same route (e.g., paging) to avoid recreating the component.
Q59. What is `Router.navigate` vs `Router.navigateByUrl`? Medium
navigate(commands, extras?) — navigates using an array of commands (relative navigation supported):
this.router.navigate(['/user', userId, 'profile'], { queryParams: { tab: 'settings' } });this.router.navigate(['../sibling'], { relativeTo: this.route }); // relativenavigateByUrl(url) — navigates using an absolute URL string:
this.router.navigateByUrl('/user/42?tab=settings');Prefer navigate() — it’s more composable and supports relative navigation. Use navigateByUrl() when you have a full URL string (e.g., from redirect).
Q60. What is `canDeactivate` guard? When do you use it? Medium
canDeactivate prevents navigation away from a route — useful to warn users about unsaved changes.
export const unsavedChangesGuard: CanDeactivateFn<EditComponent> = (component) => { if (component.hasUnsavedChanges()) { return confirm('You have unsaved changes. Leave anyway?'); } return true;};
// Route{ path: 'edit/:id', component: EditComponent, canDeactivate: [unsavedChangesGuard] }The guard can return boolean, UrlTree, or Observable<boolean> (for async confirmation dialogs).
Q61. What is `ng-template` with `ngTemplateOutlet`? Medium
ngTemplateOutlet renders a TemplateRef at a specific location, optionally with a context object.
<!-- Define template --><ng-template #loading> <div class="spinner">Loading...</div></ng-template>
<ng-template #userCard let-user="user"> <div>{{ user.name }}</div></ng-template>
<!-- Use template --><ng-container *ngTemplateOutlet="loading"></ng-container><ng-container *ngTemplateOutlet="userCard; context: { user: currentUser }"></ng-container>Useful for reusable template fragments within the same component, or passing templates as @Input() to child components for customization.
Q62. What is a standalone component? Medium
Standalone components (Angular 14+) don’t belong to any NgModule. They declare their own dependencies in their imports array — making them self-contained and more tree-shakable.
@Component({ standalone: true, selector: 'app-profile', imports: [CommonModule, RouterLink, DatePipe, UserCardComponent], template: `...`,})export class ProfileComponent {}In Angular 17+, standalone is the default when using ng generate. The entire app can be standalone:
bootstrapApplication(AppComponent, { providers: [provideRouter(routes), provideHttpClient()]});Q63. What is `TestBed`? How do you test a component? Medium
TestBed is Angular’s primary testing utility that creates an Angular testing module environment.
describe('CounterComponent', () => { let fixture: ComponentFixture<CounterComponent>; let component: CounterComponent;
beforeEach(async () => { await TestBed.configureTestingModule({ imports: [CounterComponent], // standalone component }).compileComponents();
fixture = TestBed.createComponent(CounterComponent); component = fixture.componentInstance; fixture.detectChanges(); // trigger ngOnInit + initial CD });
it('should increment count', () => { component.increment(); fixture.detectChanges(); const p = fixture.nativeElement.querySelector('p'); expect(p.textContent).toContain('1'); });});Q64. What is `fakeAsync` and `tick`? Medium
fakeAsync wraps a test in a fake async zone where time is controlled manually. tick(ms) advances the virtual clock.
it('should show result after delay', fakeAsync(() => { component.startLoading(); tick(2000); // advance 2 seconds fixture.detectChanges(); expect(component.loaded).toBe(true);}));
// For promises/microtasksit('should resolve promise', fakeAsync(() => { let result = ''; Promise.resolve('hello').then(v => result = v); flushMicrotasks(); // flush pending microtasks expect(result).toBe('hello');}));Alternative: async/await with fixture.whenStable() for real async.
Q65. What is `ngDoCheck`? When is it called? Medium
ngDoCheck fires on every change detection cycle — even when Angular doesn’t detect any changes through its normal mechanism. It runs after ngOnChanges each cycle.
Use it to detect changes Angular can’t (deeply mutated objects, custom comparison):
ngDoCheck() { if (this.items !== this.prevItems) { this.prevItems = [...this.items]; this.doSomething(); }}Caution: Runs very frequently — keep logic minimal and fast. It’s commonly used by the Angular team to implement ngFor, ngIf, etc. internally. For most app code, prefer ngOnChanges or immutable data patterns with OnPush.
Q66. What is `ngAfterViewInit`? Common use cases? Medium
ngAfterViewInit fires once after the component’s view (template) and all child views are fully initialized and available in the DOM.
Common use cases:
- Access
@ViewChildelements (first available here, not inngOnInit) - Focus an input element
- Initialize third-party libraries that need DOM (Chart.js, etc.)
- Measure element dimensions
@ViewChild('canvas') canvasRef!: ElementRef<HTMLCanvasElement>;
ngAfterViewInit() { const ctx = this.canvasRef.nativeElement.getContext('2d'); new Chart(ctx, this.chartConfig);}Important: Don’t modify state that affects the template in ngAfterViewInit — it triggers ExpressionChangedAfterItHasBeenCheckedError. Use setTimeout(()=> ..., 0) or queueMicrotask if needed.
Q67. What is Angular's `DestroyRef`? Medium
DestroyRef (Angular 16+) is an injectable that lets you register cleanup callbacks when a component/service/directive is destroyed — without implementing OnDestroy.
constructor() { const destroyRef = inject(DestroyRef); destroyRef.onDestroy(() => { console.log('Cleanup!'); this.subscription.unsubscribe(); });}Powers takeUntilDestroyed():
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
data$ = this.service.items$.pipe(takeUntilDestroyed());No need to manually manage a Subject + ngOnDestroy pattern anymore.
Q68. What is `RouterLinkActive`? Medium
routerLinkActive adds a CSS class to an element when its associated routerLink matches the current URL:
<nav> <a routerLink="/home" routerLinkActive="active">Home</a> <a routerLink="/about" routerLinkActive="active">About</a></nav>By default it also matches child routes (e.g., /about/team also activates /about). To require exact match:
<a routerLink="/home" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">Home</a>Use #rla="routerLinkActive" template variable to check .isActive programmatically.
Q69. What is `TransferState`? Medium
TransferState prevents double HTTP requests when using Angular Universal (SSR):
- Server renders the page and makes HTTP calls
- Server serializes the response data into the HTML as a
<script>tag - Browser reads that data from the DOM instead of making a new request
// Modern approach (Angular 16+): automatic with HttpClient// In app.config.server.ts:provideClientHydration()// This automatically transfers HttpClient state — no manual TransferState neededFor custom state:
const key = makeStateKey<User[]>('users');// Server: state.set(key, users)// Browser: const users = state.get(key, null)Q70. What is `inject()` function? When to use it over constructor injection? Medium
inject() is a function that retrieves a dependency from the current injection context — without needing constructor parameters.
// In component field initializer (Angular 14+)export class MyComponent { private userService = inject(UserService); private router = inject(Router);}
// In functional guardsexport const authGuard: CanActivateFn = () => { return inject(AuthService).isLoggedIn();};
// In class field initializer — enables takeUntilDestroyed()data$ = inject(UserService).getUsers().pipe( takeUntilDestroyed());Benefits over constructor: works in standalone functions (guards, resolvers), enables inline initialization, cleaner for composable utilities. Modern Angular style prefers inject().
Hard (Q71–Q103)
Section titled “Hard (Q71–Q103)”Q71. What is Angular Ivy? What improvements did it bring? Hard
Ivy is Angular’s compilation and rendering engine (default since Angular 9, replacing View Engine).
Key improvements:
- Tree-shakable — only used framework code is bundled; unused directives/pipes dropped
- Smaller bundles — “Hello World” app dropped from ~130KB to ~30KB
- Locality principle — each component compiled independently; no global compilation needed
- Improved debugging — component instance accessible via
ng.getComponent(el)in devtools - Better type checking —
strictTemplatescatches template type errors at build time - Faster builds — incremental compilation
- Foundation for standalone — enabled standalone components,
inject()function, Signals
Under Ivy, component templates are compiled to TypeScript instruction functions rather than ng_factory files.
Q72. What are Angular Signals? How do they work? Hard
Signals (Angular 16+, stable in 17+) are a reactive primitive — a synchronous, push-based reactivity system that replaces Zone.js-driven change detection.
A Signal is a wrapper around a value that notifies consumers when the value changes.
// Writable signalconst count = signal(0);console.log(count()); // read: 0count.set(5); // writecount.update(c => c + 1); // update based on previous
// Computed signal (derived, lazy, cached)const doubled = computed(() => count() * 2);
// Effect (side effect when signals change)effect(() => { console.log('Count changed:', count());});How they enable zoneless CD:
- Angular tracks which signals a template reads during rendering
- When a signal changes, only the components that read it are re-rendered
- No more Zone.js monkey-patching needed
Signals make ChangeDetectionStrategy.OnPush effectively automatic.
Q73. What is `computed()` in Angular Signals? How does it differ from `effect()`? Hard
computed() — creates a derived signal whose value is recalculated when its dependencies change. It is:
- Lazy — only recomputes when read
- Cached — same value returned if dependencies unchanged
- Synchronous — returns a value
const firstName = signal('John');const lastName = signal('Doe');const fullName = computed(() => `${firstName()} ${lastName()}`);
console.log(fullName()); // 'John Doe'firstName.set('Jane');console.log(fullName()); // 'Jane Doe' (recomputed)effect() — runs a side effect when signals it reads change. It is:
- Eager — runs immediately on change (not lazy)
- No return value — for side effects only (logging, DOM sync, analytics)
- Returns a cleanup function
effect(() => { document.title = `Count: ${count()}`; return () => { /* cleanup on re-run or destroy */ };});Don’t use effect() to update signals — use computed() or signal methods instead (can cause infinite loops).
Q74. What is the `@defer` block? When would you use it? Hard
@defer (Angular 17+) is a template syntax for lazy-loading parts of a template — components, directives, pipes are only downloaded when needed.
@defer (on viewport) { <app-heavy-chart [data]="data" />} @placeholder { <div class="placeholder">Chart loading area</div>} @loading (minimum 500ms) { <app-spinner />} @error { <p>Failed to load chart</p>}Triggers:
on idle— when browser is idleon viewport— when element enters viewporton interaction— on click/focuson hover— on hoveron timer(2s)— after delaywhen condition— when expression is truthy
Benefits: Zero JS for deferred content until trigger fires. No manual loadChildren needed. Works at template level without router involvement.
Q75. What is the new control flow syntax in Angular 17? Hard
Angular 17 introduced built-in control flow syntax that replaces *ngIf, *ngFor, *ngSwitch directives:
<!-- @if -->@if (user.isLoggedIn) { <p>Welcome, {{ user.name }}</p>} @else if (user.isGuest) { <p>Hello, Guest</p>} @else { <a routerLink="/login">Login</a>}
<!-- @for (requires track!) -->@for (item of items; track item.id) { <li>{{ item.name }}</li>} @empty { <li>No items found</li>}
<!-- @switch -->@switch (status) { @case ('active') { <span class="green">Active</span> } @case ('error') { <span class="red">Error</span> } @default { <span>Unknown</span> }}Benefits over directives:
- Better type narrowing inside
@if @emptyblock built into@fortrackis required (no accidental performance issues)- More readable, no
ng-containerhacks - Slightly smaller bundle (no directive overhead)
Q76. What is zoneless Angular? How to enable it? Hard
Zoneless Angular (experimental in v18, stabilizing in v19+) removes the Zone.js dependency entirely, using Signals for change detection triggers.
Why zoneless:
- Smaller bundle (~98KB Zone.js removed)
- Faster boot
- Better compatibility with micro-frontends and native async
- Predictable CD — only runs when signals change or
markForCheck()called
Enable in Angular 18+:
import { provideExperimentalZonelessChangeDetection } from '@angular/core';
export const appConfig: ApplicationConfig = { providers: [ provideExperimentalZonelessChangeDetection(), // remove provideZoneChangeDetection() ]};Remove Zone.js from polyfills in angular.json.
Requirements:
- Use Signals for reactive state
- Use
asyncpipe (subscribes through markForCheck) - Manual
ChangeDetectorRef.markForCheck()for non-signal state changes
Q77. What is `input()` signal function vs `@Input()` decorator? Hard
Angular 17.1+ introduced signal inputs — @Input() as a Signal:
// Old: @Input() decorator@Input() userId!: string;@Input({ required: true }) name!: string;@Input({ transform: numberAttribute }) age!: number;
// New: signal inputuserId = input<string>(); // optional, returns InputSignal<string|undefined>name = input.required<string>(); // requiredage = input(0, { transform: numberAttribute }); // with default + transformBenefits of signal inputs:
- Type-safe (required inputs can’t be undefined without explicit handling)
- Reactive — can use in
computed()andeffect() - No need for
ngOnChangesto react to input changes:
// Reactive to input change automaticallyprotected url = computed(() => `/api/user/${this.userId()}`);Q78. What is `output()` function vs `@Output()` decorator? Hard
Angular 17.3+ introduced output() function as a modern replacement for @Output() EventEmitter:
// Old@Output() selected = new EventEmitter<User>();
// Newselected = output<User>();
// Emitthis.selected.emit(user);
// With transform (outputFromObservable)import { outputFromObservable } from '@angular/core/rxjs-interop';valueChange = outputFromObservable(this.formControl.valueChanges);Benefits:
- No
EventEmitter(which extendsSubject) — cleaner outputToObservable()lets parents subscribe as Observable- Consistent with signal-based APIs
- Works with zoneless
Usage in parent template is identical to @Output() — no change needed.
Q79. What is `linkedSignal()`? (Angular 19) Hard
linkedSignal() (Angular 19, developer preview) creates a writable signal that resets when a source signal changes — solving the common pattern where you have a local selection that should reset when the data it’s based on changes.
// Problem: selectedItem should reset when items changesitems = input<Item[]>([]);// With linkedSignal:selectedItem = linkedSignal(() => this.items()[0]); // defaults to first item
// When items changes, selectedItem resets to items()[0]// But it's also writable:this.selectedItem.set(specificItem); // user selection worksReplaces the error-prone pattern of effect(() => this.selected.set(this.items()[0])).
Advanced form with equality and previous value:
selectedItem = linkedSignal<Item[], Item>({ source: this.items, computation: (newItems, previous) => newItems.find(i => i.id === previous?.value?.id) ?? newItems[0]});Q80. What is `resource()` in Angular 19? Hard
resource() (Angular 19, developer preview) is a signal-based async data fetching primitive that automatically re-fetches when its reactive dependencies (signals) change.
userId = signal(1);
userResource = resource({ request: () => ({ id: this.userId() }), // reactive params loader: async ({ request, abortSignal }) => { const res = await fetch(`/api/user/${request.id}`, { signal: abortSignal }); return res.json() as User; }});
// In template@if (userResource.isLoading()) { <app-spinner />} @else if (userResource.error()) { <p>Error: {{ userResource.error() }}</p>} @else { <p>{{ userResource.value()?.name }}</p>}rxResource() variant works with Observables instead of Promises. Automatic cancellation via AbortSignal.
Q81. What is `ChangeDetectorRef`? Methods and use cases? Hard
ChangeDetectorRef gives manual control over change detection for a component.
Key methods:
| Method | Effect |
|---|---|
markForCheck() | Marks component and ancestors as dirty — CD will run on next cycle |
detectChanges() | Runs CD synchronously on this component and children |
detach() | Removes component from CD tree completely |
reattach() | Re-attaches to CD tree |
constructor(private cdr: ChangeDetectorRef) {}
// OnPush + async data from outside Angular zonesomeCallback(data: any) { this.data = data; this.cdr.markForCheck(); // tell Angular to check this component}
// Manual CD for performance-critical componentsngOnInit() { this.cdr.detach(); // no auto CD setInterval(() => { this.data = this.getData(); this.cdr.detectChanges(); // only when we want }, 5000);}Q82. What is module federation in Angular (Micro-Frontends)? Hard
Module Federation (Webpack 5) allows multiple Angular apps (micro-frontends) to share code and load components/modules from each other at runtime.
// Host app webpack config (via @angular-architects/module-federation){ remotes: { 'mfe1': 'mfe1@http://localhost:4201/remoteEntry.js' }}
// Remote app exposes:{ exposes: { './Component': './src/app/my/my.component.ts' }}
// Host lazy-loads remote:{ path: 'mfe1', loadChildren: () => loadRemoteModule({ remoteEntry: 'http://localhost:4201/remoteEntry.js', remoteName: 'mfe1', exposedModule: './Module' }).then(m => m.MyModule)}Benefits: Independent deployments, team autonomy, technology diversity. Challenges: Shared library version conflicts, increased complexity, SSR limitations.
Q83. What is `@let` syntax in Angular 18? Hard
@let (Angular 18+) declares a local template variable from an expression — like a template-scoped const:
@let user = currentUser$ | async;@let greeting = 'Hello, ' + user?.name + '!';
@if (user) { <h1>{{ greeting }}</h1> <p>Role: {{ user.role }}</p>}Before @let you needed workarounds:
<!-- Ugly workaround --><ng-container *ngIf="currentUser$ | async as user"> <h1>Hello, {{ user.name }}</h1></ng-container>@let scopes are block-scoped (like JS let) — variables defined inside @if/@for blocks aren’t accessible outside.
Q84. Explain Angular's dependency injection tree: environment vs element injectors. Hard
Angular 14+ formalized two separate injector trees:
Environment Injectors (top of the tree):
- Root injector (providers from
bootstrapApplicationorAppModule) - Platform injector (across multiple apps on same page)
@NgModuleinjectors (lazy-loaded modules get their own)
Element Injectors (component tree):
- Each component/directive can have its own providers
- Defined via
providersin@Component({ providers: [] }) - Created when component is created, destroyed when component is destroyed
Resolution order: element injector → parent element injectors → environment injectors → root → platform.
@Component({ providers: [ { provide: DataService, useClass: LocalDataService } // shadows root DataService ]})export class FeatureComponent {}Use { providedIn: 'root' } for singletons, component providers for scoped instances.
Q85. What is `ExpressionChangedAfterItHasBeenCheckedError`? How to fix it? Hard
This error occurs in dev mode when a value used in a template changes after Angular finished checking it in the same CD cycle.
Angular verifies in dev mode by running CD twice and comparing — if values differ, it throws.
Common causes:
- Modifying state in
ngAfterViewInitthat’s bound in the template - Parent changes child’s
@InputinngAfterViewChecked - Services that emit synchronously during CD
Fixes:
Option 1: Defer the update to the next tick:
ngAfterViewInit() { Promise.resolve().then(() => { this.isLoaded = true; // runs after current CD cycle });}Option 2: Use queueMicrotask:
ngAfterViewInit() { queueMicrotask(() => this.isLoaded = true);}Option 3: Move initialization to ngOnInit (runs before the view is checked).
Option 4: Use ChangeDetectorRef.detectChanges() after the mutation.
Warning: setTimeout(fn, 0) works but causes an extra CD cycle — Promise.resolve() is cheaper.
Q86. What is Angular's `APP_INITIALIZER`? Hard
APP_INITIALIZER is a multi-provider token that runs before the application bootstraps. Useful for loading config, translations, or doing auth checks before the app renders.
function initializeApp(configService: ConfigService) { return () => configService.load(); // returns Promise or Observable}
export const appConfig: ApplicationConfig = { providers: [ { provide: APP_INITIALIZER, useFactory: initializeApp, deps: [ConfigService], multi: true } ]};Angular waits for all APP_INITIALIZER factories to resolve before rendering the root component. Multiple initializers can be registered (multi: true).
Caveat: A slow or failing initializer blocks the entire app from loading — add error handling and timeouts.
Q87. What is Angular's `@defer` `prefetch` and `when` condition? Hard
@defer supports separate triggers for loading (when JS downloads) vs showing (when rendered):
<!-- prefetch when idle, show on interaction -->@defer (on interaction; prefetch on idle) { <app-complex-form />}
<!-- Programmatic condition -->@defer (when isReady) { <app-dashboard [data]="data" />} @placeholder { <div>Loading dashboard...</div>}
<!-- Prefetch immediately but show only when user navigates -->@defer (when showDetails; prefetch on immediate) { <app-details [item]="item" />}Prefetch triggers (same as show triggers): idle, viewport, interaction, hover, immediate, timer(x), when condition.
Prefetching downloads JS early (when browser is idle) but defers rendering — best UX for content that will likely be needed soon.
Q88. What is tree shaking and how does Angular support it? Hard
Tree shaking is dead code elimination — the bundler (Webpack/Rollup/esbuild) removes unused exports from the final bundle.
How Angular enables tree shaking:
-
providedIn: 'root'services — if the service is never injected anywhere, it’s removed from the bundle. Module-levelproviders: [Service]prevents tree shaking of that service. -
Ivy’s locality — each component is self-contained. Angular only bundles directives/pipes actually used in templates (not the full
@angular/common). -
Standalone components — import only what you need per component, not entire modules.
-
ES modules — static
import/export(not dynamicrequire) enables static analysis.
// NOT tree-shakable (imported in NgModule.providers)providers: [SomeService]
// Tree-shakable@Injectable({ providedIn: 'root' })export class SomeService {}Check bundle sizes with ng build --stats-json + webpack-bundle-analyzer.
Q89. What is `Signal-based` component vs `Zone-based` component? Interop? Hard
Zone-based (traditional):
- Zone.js patches async APIs
- CD runs automatically after every async operation
DefaultorOnPushCD strategyngOnChangesfor reactive inputs
Signal-based (modern):
- CD triggered by signal mutations, not zone
- Can use
ChangeDetectionStrategy.OnPushwith zero manualmarkForCheck() input(),output(),model()signal primitives- Works with zoneless or zone-based apps
Interop: Signal-based and zone-based components can coexist in the same app. A zone app can gradually adopt signals:
// Hybrid: zone-based app, signal component@Component({ changeDetection: ChangeDetectionStrategy.OnPush, // works great with signals template: `<p>{{ count() }}</p>`})export class HybridComponent { count = signal(0);}Gradual migration: add OnPush + signals first, then remove Zone.js when ready.
Q90. What is `@angular/cdk`? Common uses? Hard
Angular CDK (Component Dev Kit) is a set of behavior primitives for building custom UI components — without opinionated styling (unlike Angular Material).
Key CDK modules:
| Module | Provides |
|---|---|
overlay | Floating panels, dropdowns, tooltips |
drag-drop | Drag and drop with ordering |
virtual-scroll | Efficient rendering of large lists |
a11y | Focus trapping, keyboard navigation, ARIA |
layout | Media query breakpoint observation |
portal | Render components outside the current view |
stepper | Step-based navigation |
table | Data table infrastructure |
// Virtual scroll exampleimport { ScrollingModule } from '@angular/cdk/scrolling';
<cdk-virtual-scroll-viewport itemSize="50" style="height: 400px"> <div *cdkVirtualFor="let item of items">{{ item }}</div></cdk-virtual-scroll-viewport>Q91. What is `HttpContext` in Angular? Hard
HttpContext (Angular 12+) lets you pass custom metadata with HTTP requests — consumed by interceptors. Replaces the hack of using custom headers for interceptor communication.
// Define a tokenconst SKIP_AUTH = new HttpContextToken<boolean>(() => false);
// Use in requestthis.http.get('/api/public', { context: new HttpContext().set(SKIP_AUTH, true)});
// Interceptor checks itexport const authInterceptor: HttpInterceptorFn = (req, next) => { if (req.context.get(SKIP_AUTH)) return next(req); // skip for public routes return next(req.clone({ headers: req.headers.set('Authorization', `Bearer ${token}`) }));};Much cleaner than using URL patterns or custom headers to communicate intent to interceptors.
Q92. How does Angular handle SSR and hydration in Angular 16+? Hard
Destructive hydration (old): Server renders HTML, then browser discards it and re-renders from scratch. Causes flickering.
Non-destructive hydration (Angular 16+): Server renders HTML → browser reuses the existing DOM nodes and attaches Angular event listeners without re-creating elements. Zero flicker.
// app.config.ts (client)provideClientHydration()
mergeApplicationConfig(appConfig, { providers: [provideServerRendering()]})Angular 17+ hydration features:
provideClientHydration(withEventReplay())— replay events that fire before hydration completes- Incremental hydration with
@defer (hydrate on ...)— defer hydration of non-critical parts - Automatic
TransferStateforHttpClientresponses
Constraints: No DOM manipulation in ngOnInit that differs between server/client. Guard with isPlatformBrowser().
Q93. What is the `model()` signal in Angular? Hard
model() (Angular 17.2+) creates a two-way bindable signal — it’s both an input and an output in one. Replaces the @Input() value + @Output() valueChange pattern for two-way binding.
// Child component@Component({ selector: 'app-counter' })export class CounterComponent { count = model(0); // or model.required<number>() increment() { this.count.update(c => c + 1); }}
// Parent template<app-counter [(count)]="myCount" />// equivalent to:<app-counter [count]="myCount" (countChange)="myCount = $event" />model() returns a ModelSignal<T> which extends WritableSignal<T> — readable and writable in both child and parent.
Q94. What is Angular Schematics? How do you create a custom schematic? Hard
Schematics are code generators and transformers used by Angular CLI (ng generate, ng add, ng update). They operate on a virtual file system — changes are only written if the schematic succeeds.
Create a custom schematic:
npm install -g @angular-devkit/schematics-clischematics blank --name=my-schematiccd my-schematicimport { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
export function mySchematic(options: any): Rule { return (tree: Tree, context: SchematicContext) => { tree.create('hello.txt', `Hello ${options.name}!`); return tree; };}schematics .:my-schematic --name=World --dry-runUse cases: Enforce team code standards, generate boilerplate, automate migrations (Angular’s own ng update uses schematics).
Q95. What is `TestBed.overrideComponent`? When is it needed? Hard
TestBed.overrideComponent replaces parts of a component’s metadata (template, providers, imports) after TestBed.configureTestingModule but before compileComponents.
TestBed.overrideComponent(MyComponent, { set: { providers: [{ provide: UserService, useClass: MockUserService }], template: '<p>Simple test template</p>' }});When needed:
- Replacing a child component’s provider without re-configuring the whole test module
- Overriding a standalone component’s imports in deep integration tests
- Simplifying templates to speed up rendering in tests
// Standalone component overrideTestBed.overrideComponent(AppComponent, { add: { imports: [RouterTestingModule] }, remove: { imports: [BrowserAnimationsModule] } // too heavy for unit test});Q96. How do you implement a dynamic component in Angular? Hard
Dynamic components are created programmatically at runtime — useful for modals, toasts, dynamic forms.
Modern approach (Angular 13+):
@Component({ template: `<ng-container #container></ng-container>`})export class HostComponent { @ViewChild('container', { read: ViewContainerRef }) container!: ViewContainerRef;
loadComponent(type: Type<any>, inputs: Record<string, any>) { this.container.clear(); const ref = this.container.createComponent(type); Object.entries(inputs).forEach(([key, value]) => { ref.setInput(key, value); }); return ref; }}
// Usagethis.loadComponent(AlertComponent, { message: 'Hello!', type: 'success' });No entryComponents array needed since Angular 13 (Ivy handles it automatically).
ComponentRef.setInput() (Angular 14+) properly triggers ngOnChanges for the dynamically set inputs.
Q97. What is `NgOptimizedImage` directive? Hard
NgOptimizedImage (Angular 15+, from @angular/common) is a directive that improves image performance by:
- Adding
loading="lazy"for offscreen images - Setting
fetchpriority="high"for LCP images - Generating
srcsetfor responsive images - Warning about missing
width/height(prevents CLS) - Supporting image loaders for CDNs (Imgix, Cloudflare, etc.)
<img ngSrc="hero.jpg" width="1280" height="720" priority /><!-- ^ priority = fetchpriority high + no lazy (LCP image) -->
<img ngSrc="avatar.jpg" width="100" height="100" /><!-- auto lazy loaded -->// With CDN loaderprovideImgixLoader('https://my-cdn.imgix.net/')// Generates: srcset="my-cdn.imgix.net/avatar.jpg?w=100 1x, ...2x"Replace <img src> with <img ngSrc> for all images — significant Core Web Vitals improvement.
Q98. What is the difference between `ViewRef`, `EmbeddedViewRef`, and `ComponentRef`? Hard
These are different types of views in Angular’s view hierarchy:
ViewRef — abstract base for a view. Provides detectChanges(), markForCheck(), destroy().
EmbeddedViewRef — a view created from a TemplateRef (via ViewContainerRef.createEmbeddedView()). Exposes context for template variables.
const tplRef: TemplateRef<any>;const viewRef: EmbeddedViewRef<any> = viewContainer.createEmbeddedView(tplRef, { $implicit: data });viewRef.context.$implicit = newData; // update contextviewRef.detectChanges();ComponentRef — a view created from a component type (via ViewContainerRef.createComponent()). Provides:
.instance— the component class instance.setInput(name, value)— set @Input signals properly.changeDetectorRef— control CD.location— ElementRef of host element
const ref = viewContainer.createComponent(MyComponent);ref.setInput('title', 'Dynamic!');ref.instance.someMethod();ref.destroy();Q99. What is `@angular/elements` (Angular Elements)? Hard
Angular Elements lets you package Angular components as Custom Elements (Web Components) — native browser elements that work without Angular.
import { createCustomElement } from '@angular/elements';
@NgModule({})export class AppModule { constructor(private injector: Injector) {}
ngDoBootstrap() { const el = createCustomElement(MyComponent, { injector: this.injector }); customElements.define('my-widget', el); }}<!-- Works in any HTML page, Vue, React, plain HTML --><my-widget title="Hello"></my-widget>Use cases:
- Embed Angular components in non-Angular apps
- Widget libraries distributed as Web Components
- Micro-frontend islands in non-Angular shells
- CMS content areas with dynamic components
Trade-off: Larger bundle size (includes Angular runtime). Better for widget libraries than micro-frontends at scale.
Q100. How does Angular's router handle scroll restoration and scroll behavior? Hard
Angular Router can manage scroll position across navigation:
// Enable scroll restorationprovideRouter(routes, withInMemoryScrolling({ scrollPositionRestoration: 'enabled', // restore on back/forward anchorScrolling: 'enabled' // support fragment (#section) links}))Options for scrollPositionRestoration:
'disabled'(default) — no scroll management'top'— always scroll to top on navigation'enabled'— restore previous scroll position on back/forward, top on new navigation
Custom scroll behavior:
import { ViewportScroller } from '@angular/common';
constructor(private vs: ViewportScroller) {}
scrollTo(anchor: string) { this.vs.scrollToAnchor(anchor);}
// Listen to scroll eventsrouter.events.pipe( filter(e => e instanceof Scroll)).subscribe((e: Scroll) => { if (e.position) this.vs.scrollToPosition(e.position); else if (e.anchor) this.vs.scrollToAnchor(e.anchor); else this.vs.scrollToPosition([0, 0]);});Q101. What is `provideRouter` with functional API vs `RouterModule.forRoot`? Hard
Angular 14+ introduced a functional router configuration API for standalone apps that’s more tree-shakable:
// Old: NgModule approach@NgModule({ imports: [RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules, scrollPositionRestoration: 'enabled' })]})
// New: functional features (tree-shakable — only bundles features you use)provideRouter( routes, withPreloading(PreloadAllModules), withInMemoryScrolling({ scrollPositionRestoration: 'enabled' }), withRouterConfig({ onSameUrlNavigation: 'reload' }), withComponentInputBinding(), // route params → @Input() automatically withViewTransitions(), // View Transitions API withDebugTracing() // dev only)withComponentInputBinding() — automatically binds route params, query params, and resolved data to component @Input() with matching names — no more injecting ActivatedRoute.
Q102. What is `withComponentInputBinding()`? How does it simplify route params? Hard
withComponentInputBinding() (Angular 16+) automatically maps route params, query params, and resolver data to @Input() properties with matching names — no ActivatedRoute injection needed.
// Route definition{ path: 'user/:id', component: UserComponent, resolve: { profile: userResolver } }
// Old way (required ActivatedRoute)constructor(private route: ActivatedRoute) {}ngOnInit() { this.id = this.route.snapshot.paramMap.get('id'); this.profile = this.route.snapshot.data['profile'];}
// New way (with withComponentInputBinding() enabled)@Input() id!: string; // from :id param@Input() page?: string; // from ?page= query param@Input() profile!: UserProfile; // from resolverWorks with signal inputs too:
id = input.required<string>(); // from :idprofile = input<UserProfile>(); // from resolverEnable once in app.config.ts:
provideRouter(routes, withComponentInputBinding())Q103. How do you optimize large Angular apps for production? Hard
Build-time optimizations:
- Production build:
ng build(AOT, tree shaking, minification, dead code elimination) OnPushchange detection everywhere- Lazy load all feature modules/components with
loadComponent/loadChildren @deferfor below-fold contentprovideRouter(withPreloading(QuicklinkStrategy))for smart preloading
Runtime optimizations:
trackByin all@for/*ngForasyncpipe over manual subscriptions- Pure pipes over method calls in templates (method calls run every CD)
NgOptimizedImagefor all images- Virtual scroll (
cdk-virtual-scroll-viewport) for long lists
Bundle analysis:
ng build --stats-jsonnpx webpack-bundle-analyzer dist/my-app/stats.jsonSignals (Angular 17+):
- Signal-based components +
OnPush→ minimal CD scope - Eventual zoneless → remove Zone.js overhead entirely
Caching:
- Service worker (
@angular/pwa) for asset caching TransferStatefor SSR to avoid double HTTP calls- HTTP caching headers on API responses