Angular Coding Interview Questions for Experienced Developers (6+ Years) 2026

Past the scenario discussion, senior Angular interviews almost always move into a live coding round — writing a custom operator, fixing a change-detection bug, or building a small piece of infrastructure from scratch. Here are the 10 coding exercises that come up most often at the 6+ years level, each with a working solution. Want the discussion-style questions first? See the Scenario-Based Questions guide.
Q1
Write a custom RxJS operator that retries a failed HTTP call with exponential backoff.
Compose it from existing operators inside a pipeable function — no need to hand-roll retry logic.
function retryWithBackoff<T>(maxRetries = 3, delayMs = 500) {
return (source: Observable<T>) =>
source.pipe(
retryWhen(errors =>
errors.pipe(
scan((count, err) => {
if (count >= maxRetries) throw err;
return count + 1;
}, 0),
delayWhen(count => timer(delayMs * Math.pow(2, count)))
)
)
);
}
// usage: this.http.get(url).pipe(retryWithBackoff(3, 500))Q2
Implement a custom structural directive, e.g. *appHasPermission='admin', that shows/hides an element based on a permission check.
A structural directive manipulates the template via TemplateRef and ViewContainerRef, adding or clearing the view based on the check.
@Directive({ selector: '[appHasPermission]' })
export class HasPermissionDirective {
@Input() set appHasPermission(role: string) {
this.permissions.has(role)
? this.vcr.createEmbeddedView(this.tpl)
: this.vcr.clear();
}
constructor(
private tpl: TemplateRef<unknown>,
private vcr: ViewContainerRef,
private permissions: PermissionsService
) {}
}Q3
Implement a debounced search box using RxJS fromEvent.
fromEvent turns the input's keyup events into a stream you can debounce and dedupe before hitting the API.
fromEvent(inputEl, 'keyup').pipe(
map((e: Event) => (e.target as HTMLInputElement).value),
debounceTime(300),
distinctUntilChanged(),
switchMap(term => this.api.search(term))
).subscribe(results => this.results = results);Q4
Write an async validator for a reactive form that checks username availability via an API call.
Async validators return an Observable/Promise of ValidationErrors, and should debounce internally so they don't fire on every keystroke.
function usernameTakenValidator(api: UserService): AsyncValidatorFn {
return (control: AbstractControl) =>
timer(300).pipe(
switchMap(() => api.checkUsername(control.value)),
map(isTaken => (isTaken ? { usernameTaken: true } : null))
);
}
// usage: new FormControl('', [], [usernameTakenValidator(this.userService)])Q5
Given an OnPush component that doesn't update when an array item is mutated, fix the code.
OnPush skips checks when a reference is mutated in place — the fix is to replace the array reference instead of mutating it.
// Before (bug): view never updates under OnPush
addItem(item: Item) {
this.items.push(item);
}
// After (fixed): new reference triggers change detection
addItem(item: Item) {
this.items = [...this.items, item];
}Q6
Write a pure pipe timeAgo that converts a timestamp into '5 minutes ago', and explain why it should be pure.
A pure pipe only recomputes when its input reference changes, which is cheap and predictable — an impure pipe re-runs on every change-detection cycle, which is wasteful for something this simple.
@Pipe({ name: 'timeAgo', pure: true })
export class TimeAgoPipe implements PipeTransform {
transform(value: string | Date): string {
const seconds = Math.floor((Date.now() - new Date(value).getTime()) / 1000);
if (seconds < 60) return `${seconds}s ago`;
const minutes = Math.floor(seconds / 60);
if (minutes < 60) return `${minutes}m ago`;
const hours = Math.floor(minutes / 60);
return hours < 24 ? `${hours}h ago` : `${Math.floor(hours / 24)}d ago`;
}
}Q7
Set up a multi-provider dependency injection token, similar to how HTTP_INTERCEPTORS works, for pluggable logging services.
multi: true tells Angular to collect every provider registered against the token into an array, instead of the last one winning.
export const LOGGERS = new InjectionToken<Logger[]>('LOGGERS');
@NgModule({
providers: [
{ provide: LOGGERS, useClass: ConsoleLogger, multi: true },
{ provide: LOGGERS, useClass: RemoteLogger, multi: true },
],
})
export class LoggingModule {}
// consumer: constructor(@Inject(LOGGERS) private loggers: Logger[]) {}Q8
Fix a component with a setInterval and subscription that isn't cleaned up, causing a memory leak.
Angular 16+'s takeUntilDestroyed() ties the stream's lifetime to the component automatically, replacing the manual destroy$ Subject pattern.
export class TickerComponent {
private destroyRef = inject(DestroyRef);
ngOnInit() {
interval(1000)
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => this.tick++);
}
}Q9
Implement a large list (10,000+ items) efficiently using Angular CDK's virtual scroll.
cdk-virtual-scroll-viewport renders only the items in (and just around) the visible viewport, keeping DOM node count constant regardless of list length.
<cdk-virtual-scroll-viewport itemSize="48" class="viewport">
<div *cdkVirtualFor="let item of items; trackBy: trackById">
{{ item.name }}
</div>
</cdk-virtual-scroll-viewport>Q10
Build a minimal state store from scratch using BehaviorSubject, with select() and dispatch()-style methods.
This is the pattern most 'roll your own NgRx' interview questions are testing — immutable updates driven through a single BehaviorSubject.
@Injectable({ providedIn: 'root' })
export class Store<T> {
private state$: BehaviorSubject<T>;
constructor(initial: T) { this.state$ = new BehaviorSubject(initial); }
select<K>(selector: (state: T) => K): Observable<K> {
return this.state$.pipe(map(selector), distinctUntilChanged());
}
setState(patch: Partial<T>) {
this.state$.next({ ...this.state$.value, ...patch });
}
}Final Thoughts
Coding rounds test whether you can turn the concepts you can talk about into working code under time pressure. Type these out by hand rather than reading them — muscle memory matters more than recognition here. Pair this with the scenario-based questions and build both muscles with mock interviews at Roundexa.com.
Ready to Practice?
Take a free AI mock interview on Roundexa and get instant, actionable feedback before the real one.
Practice on RoundexaRead Next
Angular Scenario-Based Interview Questions for Experienced Developers (6+ Years) 2026
Angular interviews at the senior level test judgment, not syntax. This guide covers 18 scenario-based questions on architecture, performance, RxJS, change detection, forms, and migration.
Java Interview Questions and Answers for Freshers 2026
Java interviews follow patterns. This guide covers the questions that come up most — OOP, Collections, Exception Handling, multithreading — with answers that are easy to understand and explain.