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
The complete Java interview guide for freshers: JVM basics, OOP, Strings, exceptions, Collections and HashMap internals, Java 8+ streams, modern Java, multithreading, memory, 8 coding programs and 30 questions with answers. Every code example tested.