Top Angular coding Interview Questions and Answers (2026 Edition)
Angular interviews can feel overwhelming there's a lot to cover and interviewers love going deep. The good news is that most questions circle around the same core ideas.
Work through this list, understand the why behind each answer, and you'll walk in confident.
Core Angular Concepts
1. What is the difference between AngularJS and Angular?
This one comes up a lot, especially if your resume mentions both. AngularJS (v1.x) was built in the early days of single-page apps it used JavaScript, a controller/scope model, and MVC architecture.
Angular (v2 and beyond) is essentially a completely different framework that just kept the name. It was rewritten in TypeScript, shifted to a component-based model, and introduced the Ivy compiler which made apps significantly faster and smaller.
If an interviewer asks this, they're checking whether you know Angular has a history, not just a present.
2. Explain the component lifecycle hooks.
Think of lifecycle hooks as Angular tapping you on the shoulder and saying "hey, something just happened to your component want to do something about it?" They fire in this order:
- ngOnChanges :called first, whenever a bound @Input() value changes
- ngOnInit : your go-to hook for setup logic; runs once after the first ngOnChanges
- ngDoCheck : runs on every change detection cycle; use it carefully, it fires a lot
- ngAfterContentInit / ngAfterContentChecked: triggered after Angular projects external content via ng-content
- ngAfterViewInit / ngAfterViewChecked: triggered after the component's own view and all child views are fully rendered
- ngOnDestroy: your cleanup hook; always unsubscribe from observables here to avoid memory leaks
3. What is Dependency Injection (DI) in Angular?
DI sounds fancy but the idea is simple: instead of a class creating its own dependencies (like a service), Angular hands them over from the outside. This makes your code easier to test and swap out. You tell Angular a service is injectable with @Injectable(), and using providedIn: 'root' creates one shared instance across the whole app.
@Injectable({ providedIn: 'root' })
export class DataService {
getData() { ... }
}
Directives & Templates
4. What is the difference between structural and attribute directives?
The easiest way to remember this: structural directives change the shape of the DOM (they add or remove elements), while attribute directives change the look or behavior of something already there.
- Structural : *ngIf removes an element from the DOM entirely; *ngFor stamps out multiple elements
- Attribute:
ngClassadds CSS classes;ngStyleapplies inline styles
5. How do you create a custom directive?
Say you want to highlight any element when the user hovers over it. Instead of writing that logic in every component, you create a directive once and reuse it anywhere. Here's a real example:
@Directive({ selector: '[appHighlight]' })
export class HighlightDirective {
constructor(private el: ElementRef) {}
@HostListener('mouseenter') onMouseEnter() {
this.el.nativeElement.style.backgroundColor = 'yellow';
}
@HostListener('mouseleave') onMouseLeave() {
this.el.nativeElement.style.backgroundColor = '';
}
}
Drop it on any element with <p appHighlight>Hover over me</p> and it just works.
Data Binding & Component Communication
6. What are the types of data binding in Angular?
Data binding is how your component class and HTML template talk to each other. Angular gives you four ways to do it:
- Interpolation : {{ value }} simplest way to display data in the template
- Property binding: [property]="value" pushes data from the class into a DOM property
- Event binding : (event)="handler()" listens for user actions and calls a method
- Two-way binding : [(ngModel)]="property" keeps the input and the class in sync; needs FormsModule imported
7. How do parent and child components communicate?
Component communication is one of those topics that trips up junior developers but becomes second nature once you've built a few features.
- Parent → Child: Use @Input() to pass data down from the parent
- Child → Parent: Use @Output() withEventEmitter to send events back up
- Sibling / unrelated components: A shared service with a Subject or BehaviorSubject is your best friend here
RxJS & Observables
8. What is the difference between an Observable and a Promise?
Promises and Observables both handle async code, but they behave very differently under the hood. Here's a quick side-by-side:
| Observable | Promise | |
|---|---|---|
| Execution | Lazy — nothing runs until you subscribe | Eager — starts immediately when created |
| Values | Can emit multiple values over time | Resolves once with a single value |
| Cancellable | Yes, just unsubscribe | No built-in cancellation |
| Operators | Huge RxJS operator library | Basic chaining with .then() |
9. How do you prevent memory leaks with observables?
Forgetting to unsubscribe is one of the most common bugs in Angular apps. The subscription keeps running even after the component is gone, wasting memory. Three solid ways to avoid this:
- Use the async pipe in your template Angular automatically subscribes and unsubscribes for you
- Use takeUntil with a destroy subject clean and works great for multiple subscriptions
- Use take(1) when you only need the first emitted value
private destroy$ = new Subject<void>();
ngOnInit() {
this.dataService.getData()
.pipe(takeUntil(this.destroy$))
.subscribe(data => this.data = data);
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
10. How do you retry an HTTP request on failure?
Network requests fail sometimes. RxJS makes it easy to automatically retry before giving up no manual loop needed.
this.http.get(url).pipe(
retry(3),
catchError(err => {
console.error('Request failed after 3 retries', err);
return throwError(() => err);
})
).subscribe(data => console.log(data));
Advanced Topics
11. Explain Angular's change detection strategies.
Angular watches for changes in your app and updates the view accordingly. By default it's aggressive — it checks everything. But you can make it smarter:
- Default: Angular walks the entire component tree on every browser event, timer tick, or async call. Safe, but can be slow in large apps
- OnPush: Angular only re-checks the component when an
@Input()reference changes, an internal event fires, or you manually callChangeDetectorRef.markForCheck()
Switching your components to OnPush is one of the easiest wins for app performance, especially in component-heavy dashboards.
12. How does lazy loading work in Angular?
Nobody wants to download the entire app upfront. Lazy loading lets Angular fetch feature modules only when a user actually navigates to them, keeping the initial bundle small and load times fast.
// app-routing.module.ts
const routes: Routes = [
{
path: 'dashboard',
loadChildren: () =>
import('./dashboard/dashboard.module').then(m => m.DashboardModule)
}
];
If you're using standalone components (Angular 15+), use loadComponent directly no module needed:
{
path: 'profile',
loadComponent: () =>
import('./profile/profile.component').then(c => c.ProfileComponent)
}
13. What is Angular Universal?
- Angular runs in the browser by default, which means search engine crawlers often see a blank page before JavaScript kicks in.
- Angular Universal solves this with Server-Side Rendering (SSR) the server builds the full HTML first, sends it to the browser, then Angular takes over.
- The result is faster perceived load times and much better SEO. From Angular 17 onwards, SSR ships with the CLI out of the box when you run
ng new --ssr.
Performance Optimization
14. How do you optimize *ngFor performance?
Without trackBy, Angular tears down and rebuilds the entire list whenever the data array changes even if only one item changed.
Add trackBy and Angular knows which items are new, updated, or removed, so it only touches what needs touching.
<div *ngFor="let item of items; trackBy: trackById">{{ item.name }}</div>
trackById(index: number, item: any): number {
return item.id;
}
15. What is the difference between pure and impure pipes?
This is a subtle but important distinction for performance:
- Pure pipes: Angular only reruns the pipe when the input value actually changes. This is the default and what you want 99% of the time
- Impure pipes :Angular reruns them on every change detection cycle, regardless of whether the input changed. They're powerful but can noticeably slow down your app if overused
Angular Signals (v16+)
16. What are Angular Signals and why do they matter?
Signals are Angular's newer approach to reactivity and honestly one of the most exciting recent additions.
Instead of relying on Zone.js to detect changes across the whole app, a signal tracks exactly who depends on it and only notifies those dependents when its value changes.
It's more predictable and more performant.
import { signal, computed, effect } from '@angular/core';
count = signal(0);
doubled = computed(() => this.count() * 2);
increment() {
this.count.update(c => c + 1);
}
Signals pair perfectly with OnPush components and are the building blocks for Angular's upcoming Zoneless mode so it's worth getting comfortable with them now.
New Control Flow Syntax (v17+)
17. What is Angular's new built-in control flow?
Angular 17 shipped a new template syntax that replaces the old structural directives. It's cleaner, easier to read, and adds some useful new features like the @empty block for empty lists. You'll start seeing this in most new codebases:
<!-- Replaces *ngIf -->
@if (isLoggedIn) {
<app-dashboard />
} @else {
<app-login />
}
<!-- Replaces *ngFor, with built-in empty state -->
@for (item of items; track item.id) {
<li>{{ item.name }}</li>
} @empty {
<li>No items found.</li>
}
<!-- Replaces ngSwitch -->
@switch (status) {
@case ('active') { <span>Active</span> }
@case ('inactive') { <span>Inactive</span> }
@default { <span>Unknown</span> }
}
Testing
18. How do you test a component with dependencies?
The key to unit testing in Angular is isolation you want to test the component, not its dependencies. Use TestBed to set up a minimal testing environment and swap real services with mock versions:
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [MyComponent],
providers: [{ provide: MyService, useClass: MockService }]
});
fixture = TestBed.createComponent(MyComponent);
});
it('should display fetched data', () => {
fixture.detectChanges();
const compiled = fixture.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Hello');
});
Real-World Scenarios
19. How do you implement a debounced search input?
Firing an API call on every single keystroke is a quick way to hammer your backend. A debounced search waits until the user pauses typing before sending the request.
switchMap is the critical piece here it cancels the previous in-flight request if the user types again, so you never process stale results.
searchTerm = new FormControl('');
ngOnInit() {
this.searchTerm.valueChanges.pipe(
debounceTime(300),
distinctUntilChanged(),
switchMap(term => this.searchService.search(term))
).subscribe(results => this.results = results);
}
20. How do you add authentication headers using an HTTP interceptor?
Rather than manually adding an auth token to every single HTTP call, interceptors let you do it once in one place. Every outgoing request passes through your interceptor automatically.
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private authService: AuthService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const token = this.authService.getToken();
if (token) {
const authReq = req.clone({
setHeaders: { Authorization: `Bearer ${token}` }
});
return next.handle(authReq);
}
return next.handle(req);
}
}
Wire it up in your providers array: { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }. The multi: true flag is important it tells Angular you're adding to a list of interceptors, not replacing them.
Common Pitfalls
21. Why does ngModel require FormsModule?
ngModel isn't a built-in HTML attribute it's an Angular directive that lives inside FormsModule. If you forget to import FormsModule in your module (or in a standalone component's imports array), Angular simply won't recognize it and you'll get a template parse error. It's a small thing but it trips up a surprising number of developers.
22. How do you fix ExpressionChangedAfterItHasBeenCheckedError?
This error shows up when something in your code changes a binding value after Angular has already finished checking it for that cycle. It only appears in development mode, but it's Angular warning you about a real design issue. Here's how to deal with it:
- Move the state change into ngAfterViewInit and immediately call ChangeDetectorRef.detectChanges() to trigger another check
- Wrap the change in setTimeout() to push it to the next event loop tick works but treat it as a last resort
- The cleanest fix is to restructure your code so the value is set before change detection starts, ideally in ngOnInit
Before You Walk Into That Interview
Technical knowledge only gets you so far interviewers also want to see how you think. A few things worth focusing on in your last few days of prep:
- RxJS operators: get comfortable with map,filter, switchMap,mergeMap, combineLatest, catchError, and debounceTime; know when to use each
- Signals vs. RxJS: understand the difference and when one makes more sense than the other
- Change detection: be able to explain Default vs OnPush and why Signals are moving Angular away from Zone.js
- Standalone components: know how to bootstrap an app and declare imports without NgModules
- Build something small: a search-as-you-type feature, a simple auth flow, or a todo list will make these concepts stick far better than just reading about them







