Interview questionsAngular
Angular interview questions and answers
30 Angular questions picked from Codejump's quizzes, from the basics a junior is expected to know to the details that trip up senior candidates. Each one comes with its answer and an explanation — try to answer before you open it.
Junior level Angular questions
1.How do you listen to a button click event in an Angular template?
javascript@Component({ selector: 'app-counter', template: ` <p>Count: {{ count }}</p> <button (click)="increment()">+1</button> `, standalone: true, }) export class CounterComponent { count = 0; increment() { this.count++; } }- A.[click]="handleClick()"
- B.{{click}}="handleClick()"
- C.*click="handleClick()"
- D.(click)="handleClick()"
Show answer
Answer: D. (click)="handleClick()"
Parentheses syntax(event)="handler()"is Angular's event binding. It listens to the specified DOM event and calls the handler method on the component. You can also pass the event object:(click)="handleClick($event)".From the quiz Component Basics
2.How do you inject a service into an Angular component?
javascript@Component({ selector: 'app-profile', template: `<p>{{ user?.name }}</p>`, standalone: true, }) export class ProfileComponent implements OnInit { user?: User; constructor( private userService: UserService, private router: Router, ) {} ngOnInit() { this.userService.getCurrentUser().subscribe(u => this.user = u); } }- A.By declaring the service type in the constructor parameters
- B.By importing the service module using ES6 imports only
- C.By calling ServiceClass.getInstance() in the component
- D.By using @Inject(ServiceClass) in the template
Show answer
Answer: A. By declaring the service type in the constructor parameters
Angular uses constructor injection — services are provided via the constructor's parameter list. Angular reads the TypeScript types (via Reflect metadata) to know what to inject. In Angular 14+, you can also use theinject()function as an alternative:From the quiz Injectable & Providers
3.How does a child component communicate with its parent?
javascript// child.component.ts @Component({ selector: 'app-counter', template: ` <p>Count: {{ count }}</p> <button (click)="increment()">+</button> ` }) export class CounterComponent { @Input() count = 0; @Output() countChange = new EventEmitter<number>(); increment() { this.countChange.emit(this.count + 1); } } // parent template <app-counter [count]="myCount" (countChange)="myCount = $event" />- A.@Input() receives data from parent to child; @Output() emits events from child to parent via EventEmitter
- B.@Input() and @Output() are optional — Angular automatically detects public properties
- C.@Output() only works with EventEmitter<void>
- D.The parent must use a shared service — it cannot listen to the child's events directly
Show answer
Answer: A. @Input() receives data from parent to child; @Output() emits events from child to parent via EventEmitter
Modern Angular 17.1+ withmodel():From the quiz Angular Basics
4.What is the purpose of NgRx selectors?
javascriptimport { createSelector, createFeatureSelector } from '@ngrx/store'; // Feature selector — selects the 'users' slice of root state export const selectUsersState = createFeatureSelector<UsersState>('users'); // Derived selectors — memoized export const selectAllUsers = createSelector( selectUsersState, state => state.users ); export const selectUsersLoading = createSelector( selectUsersState, state => state.loading ); export const selectActiveUsers = createSelector( selectAllUsers, users => users.filter(u => u.active) // only re-computed when selectAllUsers changes ); // Component usage: @Component({ standalone: true }) export class UsersComponent { users$ = inject(Store).select(selectAllUsers); loading$ = inject(Store).select(selectUsersLoading); activeUsers$ = inject(Store).select(selectActiveUsers); }- A.To dispatch actions to the store
- B.To define what state slices reducers manage
- C.To derive and memoize specific slices of state for components to consume
- D.To configure which components receive store updates
Show answer
Answer: C. To derive and memoize specific slices of state for components to consume
Selectors are memoized pure functions that select and derive data from the store: Selectors are memoized — if the input state slice didn't change, the function is not re-run and the previous result is returned.From the quiz Actions & Reducers
5.How do you access a route parameter (e.g.,
/users/:id) in a component?javascript@Component({ selector: 'app-user-detail', template: `<p>User ID: {{ userId }}</p>`, standalone: true, }) export class UserDetailComponent implements OnInit { userId?: string; // Option 1: snapshot — one-time read (use when component is not reused for same route) private route = inject(ActivatedRoute); ngOnInit() { this.userId = this.route.snapshot.params['id']; // Option 2: Observable — reactive (use when navigating between same route with different params) this.route.params.subscribe(params => { this.userId = params['id']; this.loadUser(this.userId!); }); // Option 3: Signal (Angular 16+) this.userId = toSignal(this.route.params.pipe(map(p => p['id'])), { initialValue: '' })(); } }- A.Via this.route.params which is a regular object with parameter values
- B.Via @Input() id: string automatically bound by the router
- C.Via this.router.params.id directly on the Router service
- D.Via inject(ActivatedRoute).snapshot.params for one-time access, or inject(ActivatedRoute).params Observable for reactive access
Show answer
Answer: D. Via inject(ActivatedRoute).snapshot.params for one-time access, or inject(ActivatedRoute).params Observable for reactive access
Route parameters are available viaActivatedRoute: In Angular 16+ withwithComponentInputBinding(), route params are automatically bound to@Input():From the quiz Route Config & Links
6.What does
HttpClient.get<User[]>('/api/users')return?javascript@Injectable({ providedIn: 'root' }) class UserService { constructor(private http: HttpClient) {} getUsers(): Observable<User[]> { // No request yet — just a definition return this.http.get<User[]>('/api/users'); } } @Component({ ... }) class UserListComponent implements OnInit { users: User[] = []; constructor(private userService: UserService) {} ngOnInit() { // Request fires HERE when subscribe() is called this.userService.getUsers().subscribe(users => { this.users = users; }); // Or let async pipe handle it: this.users$ = this.userService.getUsers(); // no request yet } }- A.A Promise<User[]>
- B.An Observable<User[]> that makes the HTTP request only when subscribed
- C.A User[] array directly
- D.A Subject<User[]> that replays the response to late subscribers
Show answer
Answer: B. An Observable<User[]> that makes the HTTP request only when subscribed
AllHttpClientmethods return cold Observables — the HTTP request is only made when yousubscribe()(or useasyncpipe).From the quiz Observables & HTTP
7.What does the
selectorproperty of@Componentdefine?javascript@Component({ selector: 'app-card', // used as <app-card></app-card> template: `<div class="card"><ng-content></ng-content></div>`, standalone: true, }) export class CardComponent {}- A.The CSS file to apply to the component
- B.The name used to embed the component in other templates
- C.The route path of the component
- D.The Angular module that owns the component
Show answer
Answer: B. The name used to embed the component in other templates
Theselectoris the custom HTML element name (or CSS selector) used to insert the component into templates. When Angular parses a template and finds a matching selector, it renders the component there. In another template:<app-card>Hello</app-card>Selectors can also be attribute-based ([appCard]) or class-based (.app-card), but element selectors are the most common.From the quiz Component Basics
8.What is the
inject()function and when can it be used?javascript@Component({ selector: 'app-dashboard', template: `...`, standalone: true, }) export class DashboardComponent { // Works in field initializers (injection context is active) private userService = inject(UserService); private router = inject(Router); private analyticsService = inject(AnalyticsService, { optional: true }); // Constructor is optional — can be omitted entirely }- A.It is the same as @Inject() decorator but for standalone components only
- B.It replaces the constructor entirely and cannot be combined with constructor injection
- C.It is used only inside @NgModule providers for forward references
- D.It retrieves a dependency from the current injection context — usable in constructor, field initializers, and factory functions
Show answer
Answer: D. It retrieves a dependency from the current injection context — usable in constructor, field initializers, and factory functions
inject()is a functional API for dependency injection that works in any injection context (constructor, class field initializers,runInInjectionContext, etc.).inject()also works in composable functions (similar to React hooks):From the quiz Injectable & Providers
9.What is the correct syntax for the control flow introduced in Angular 17?
javascript@Component({ template: ` <!-- Conditional rendering --> @if (isLoggedIn) { <p>Welcome, {{ username }}!</p> } @else { <p>Please sign in.</p> } <!-- List rendering --> @for (item of items; track item.id) { <li>{{ item.name }}</li> } @empty { <li>No items found.</li> } ` })- A.This syntax is invalid — Angular only uses *ngIf and *ngFor
- B.@else is not supported — you must use two separate @if blocks with inverse conditions
- C.track in @for is optional and has no effect on performance
- D.@if / @for is the new built-in control flow syntax in Angular 17+, replacing *ngIf and *ngFor. It does not require importing CommonModule or NgIf/NgFor
Show answer
Answer: D. @if / @for is the new built-in control flow syntax in Angular 17+, replacing *ngIf and *ngFor. It does not require importing CommonModule or NgIf/NgFor
Angular 17 introduced built-in control flow as a language-level syntax — no directives, no imports needed:trackis REQUIRED in@for— it identifies each item for DOM reconciliation (like React'skey). Usingtrack $indexis allowed buttrack item.idis preferred for stable identity.From the quiz Angular Basics
10.How do you read state from the store in a component?
javascript@Component({ selector: 'app-cart', template: ` @if (loading$ | async) { <p>Loading...</p> } @else { @for (item of items$ | async; track item.id) { <app-cart-item [item]="item" /> } } `, standalone: true, imports: [AsyncPipe, CartItemComponent], }) export class CartComponent { private store = inject(Store); items$ = this.store.select(selectCartItems); loading$ = this.store.select(selectCartLoading); totalPrice$ = this.store.select(selectCartTotalPrice); removeItem(itemId: string) { this.store.dispatch(removeFromCart({ itemId })); } }- A.const state = Store.getState()
- B.Subscribe to StoreEvents.onChange in ngOnInit
- C.inject(Store).state which is a plain object
- D.inject(Store).select(selector) which returns an Observable
Show answer
Answer: D. inject(Store).select(selector) which returns an Observable
Store.select()returns an Observable of the selected state slice, updated whenever the relevant state changes:From the quiz Actions & Reducers
Mid level Angular questions
11.What does
@ViewChild(ChildComponent)give you access to?javascript@Component({ selector: 'app-parent', template: `<app-timer #timer />`, standalone: true, imports: [TimerComponent], }) export class ParentComponent implements AfterViewInit { @ViewChild(TimerComponent) timer!: TimerComponent; // OR by template ref: @ViewChild('timer') timer!: TimerComponent; ngAfterViewInit() { this.timer.start(); // ✅ available here } }- A.The HTML element wrapping the child component
- B.All matching child component instances in the view
- C.The first matching child component instance in the view
- D.The child component's @Input() declarations
Show answer
Answer: C. The first matching child component instance in the view
@ViewChildqueries the component's view (its own template) and returns the first matching element, directive, or component instance. It is available afterngAfterViewInit. To query all matching children, use@ViewChildren, which returns aQueryList<T>.From the quiz Lifecycle & Projection
12.What problem does the
forRoot()/forChild()pattern solve?javascript@NgModule() export class AuthModule { static forRoot(config: AuthConfig): ModuleWithProviders<AuthModule> { return { ngModule: AuthModule, providers: [ AuthService, // singleton at root level { provide: AUTH_CONFIG, useValue: config }, ], }; } static forChild(): ModuleWithProviders<AuthModule> { return { ngModule: AuthModule, providers: [], // no services — reuse root singleton }; } } // AppModule @NgModule({ imports: [AuthModule.forRoot({ realm: 'codejump' })] }) export class AppModule {} // FeatureModule (lazy) @NgModule({ imports: [AuthModule.forChild()] }) export class ProfileModule {}- A.It sets up routing for the root module
- B.It allows a module to be used in both eager and lazy-loaded contexts with correct service scoping
- C.It enables tree-shaking for NgModule providers
- D.It registers component declarations for both root and child routes
Show answer
Answer: B. It allows a module to be used in both eager and lazy-loaded contexts with correct service scoping
forRoot()provides services and configuration at the root level (singleton), whileforChild()provides only the directives/pipes/components for child feature modules, without re-providing services (which would create duplicate instances). In standalone apps, this pattern is less common — useprovideAuth(config)functions withApplicationConfig.From the quiz Hierarchical Injectors
13.How do you configure routing in a standalone Angular 19 application?
javascript// app.routes.ts export const routes: Routes = [ { path: '', redirectTo: '/home', pathMatch: 'full' }, { path: 'home', component: HomeComponent }, { path: 'users', loadComponent: () => import('./users/users.component').then(m => m.UsersComponent), }, { path: 'users/:id', component: UserDetailComponent }, { path: '**', component: NotFoundComponent }, ]; // app.config.ts export const appConfig: ApplicationConfig = { providers: [provideRouter(routes)], };- A.loadComponent is not a valid Routes property — use loadChildren instead
- B.The configuration is correct. loadComponent lazy-loads a standalone component; path: '**' is the wildcard route for 404s; route order matters (most specific first)
- C.pathMatch: 'full' is the default value — there's no need to specify it
- D.Route parameters like :id require an additional params: configuration
Show answer
Answer: B. The configuration is correct. loadComponent lazy-loads a standalone component; path: '**' is the wildcard route for 404s; route order matters (most specific first)
Route order matters: Angular matches routes top-to-bottom.path: '**'must be LAST. ---From the quiz Templates & Binding
14.What does
on()insidecreateReducerdo?javascriptimport { createReducer, on } from '@ngrx/store'; export const cartReducer = createReducer( initialCartState, // Single action handler on(addToCart, (state, { item }) => ({ ...state, items: [...state.items, item], })), // Multiple actions with same handler on(clearCart, resetCart, state => ({ ...state, items: [], total: 0, })), // Handler with complex logic on(applyDiscount, (state, { code, percentage }) => { const discountedTotal = state.subtotal * (1 - percentage / 100); return { ...state, coupon: code, discount: percentage, total: discountedTotal, }; }), );- A.Subscribes to an Observable for each action type
- B.Runs the provided function asynchronously when the action is dispatched
- C.Creates a new action and links it to a reducer handler
- D.Registers a state transformation function for one or more action types
Show answer
Answer: D. Registers a state transformation function for one or more action types
on(action, handler)registers a handler function that computes new state when the specified action is dispatched: Eachon()call corresponds to one or more action types. When an action is dispatched, NgRx finds the matchingon()handlers and applies them in order.From the quiz Effects & Selectors
15.What is the purpose of a route resolver?
javascript// user.resolver.ts export const userResolver: ResolveFn<User> = (route) => { const userService = inject(UserService); const router = inject(Router); return userService.getUser(route.params['id']).pipe( catchError(() => { router.navigate(['/not-found']); return EMPTY; }), ); }; // Route config: { path: 'users/:id', component: UserDetailComponent, resolve: { user: userResolver }, } // Component — data is available synchronously: @Component({ standalone: true }) export class UserDetailComponent implements OnInit { private route = inject(ActivatedRoute); user!: User; ngOnInit() { this.user = this.route.snapshot.data['user']; // OR with Angular 16+ withComponentInputBinding(): // @Input() user!: User; // auto-bound from resolver } }- A.To pre-fetch data before the route's component is created, making the data available immediately in the component
- B.To validate URL parameters before the route activates
- C.To transform the URL before the router processes it
- D.To determine which component to render based on screen size
Show answer
Answer: A. To pre-fetch data before the route's component is created, making the data available immediately in the component
Resolvers pre-fetch data during navigation so the component receives it immediately viaActivatedRoute.data:From the quiz Guards & Lazy Loading
16.What does
ReplaySubject(3)do?javascriptimport { ReplaySubject } from 'rxjs'; const replay$ = new ReplaySubject<string>(3); replay$.next('a'); replay$.next('b'); replay$.next('c'); replay$.next('d'); // 'a' is dropped from buffer (buffer size is 3) // New subscriber immediately receives buffered values: replay$.subscribe(v => console.log('New subscriber:', v)); // "New subscriber: b" // "New subscriber: c" // "New subscriber: d" // Useful for caching with shareReplay: const cachedUsers$ = this.http.get<User[]>('/api/users').pipe( shareReplay(1), // ReplaySubject(1) under the hood ); // All subscribers get the same cached response- A.Buffers the last 3 emitted values and replays all of them to each new subscriber
- B.Replays the last 3 milliseconds of emissions to new subscribers
- C.Limits the stream to only 3 total emissions before completing
- D.Creates a Subject that shares emissions between 3 subscribers maximum
Show answer
Answer: A. Buffers the last 3 emitted values and replays all of them to each new subscriber
ReplaySubject(n)buffers the lastnemissions and replays them synchronously to any new subscriber.From the quiz Operators & Subjects
17.What does
<ng-content>do inside a component's template?javascript@Component({ selector: 'app-card', template: ` <div class="card"> <ng-content select="[card-header]" /> <div class="body"> <ng-content /> </div> </div> `, standalone: true, }) export class CardComponent {} // Usage: // <app-card> // <h2 card-header>Title</h2> // <p>Body content here</p> // </app-card>- A.It fetches content from an HTTP endpoint
- B.It marks the location for router-outlet rendering
- C.It creates a portal for modal dialogs
- D.It acts as a slot that renders the content passed between the component's opening and closing tags
Show answer
Answer: D. It acts as a slot that renders the content passed between the component's opening and closing tags
<ng-content>is Angulars content projection mechanism, similar to web component slots. The content defined between the components tags in the parent is projected into the<ng-content>slot. Theselectattribute enables multi-slot projection, allowing different pieces of projected content to land in different slots.From the quiz Lifecycle & Projection
18.When Angular looks up a dependency, what is the order it searches injectors?
javascriptElement injector (component/directive providers) → Parent element injectors (up the component tree) → Module injector (for NgModule-based apps) → Root injector (providedIn: 'root') → Platform injector → Null injector (throws NullInjectorError)- A.Component injector → Parent component injectors → Module injector → Root injector → Platform injector → Null injector
- B.Root injector → Platform injector → Null injector
- C.Service file → Root injector → Component injector
- D.NgModule injector → Component injector → Root injector
Show answer
Answer: A. Component injector → Parent component injectors → Module injector → Root injector → Platform injector → Null injector
Angular has a hierarchical injector tree. When a token is not found, Angular walks up the tree:From the quiz Hierarchical Injectors
19.Why use lazy loading and how does it differ from eager loading?
javascriptconst routes: Routes = [ // Eager loading — bundled in main chunk: { path: 'home', component: HomeComponent }, // Lazy loading — separate chunk downloaded on demand: { path: 'dashboard', loadComponent: () => import('./dashboard/dashboard.component').then(m => m.DashboardComponent), }, { path: 'admin', loadChildren: () => import('./admin/admin.routes').then(m => m.adminRoutes), }, ];- A.Lazy loading slows navigation down — Angular must wait for the chunk to download before it can render the route
- B.loadComponent and loadChildren are identical
- C.Lazy loading shrinks the initial bundle (faster time-to-interactive) by splitting the app into on-demand chunks. Navigation is slightly delayed on first access, but Angular can preload the chunks in the background
- D.Lazy loading disables the route guard on the affected route
Show answer
Answer: C. Lazy loading shrinks the initial bundle (faster time-to-interactive) by splitting the app into on-demand chunks. Navigation is slightly delayed on first access, but Angular can preload the chunks in the background
loadComponentvsloadChildren:loadComponent— lazy-loads a single standalone componentloadChildren— lazy-loads a set of routes (previously used for NgModules, now for route arrays) ---
From the quiz Templates & Binding
20.What does the NgRx Store DevTools extension enable?
javascript// Setup: provideStoreDevtools({ maxAge: 25, // retain last 25 actions logOnly: !isDevMode(), // disable DevTools in production autoPause: true, // pause recording when not in use trace: false, // include stack trace traceLimit: 75, // limit stack trace lines connectInZone: true, // ensure CD after DevTools actions }) // Features: // 1. Action log: see every dispatched action with its payload // 2. State diff: see exactly what changed after each action // 3. Time-travel: jump back to any previous state // 4. Replay: re-dispatch actions from a specific point // 5. Import/Export: save and load action logs for bug reports // 6. Action filtering: filter the action log for debugging- A.Visual profiling of Angular change detection cycles
- B.HTTP request monitoring for Effects
- C.Automatic unit test generation for NgRx actions and reducers
- D.Time-travel debugging — inspecting dispatched actions and state snapshots, replaying/rewinding actions
Show answer
Answer: D. Time-travel debugging — inspecting dispatched actions and state snapshots, replaying/rewinding actions
NgRx DevTools (requires Redux DevTools browser extension) provides: Pair with NgRx Router Store to see navigation as actions:From the quiz Effects & Selectors
Senior level Angular questions
21.What is the difference between a pure pipe and an impure pipe?
javascript// Pure pipe (default) — only re-runs when input reference changes: @Pipe({ name: 'filter', standalone: true }) export class FilterPipe implements PipeTransform { transform(items: Item[], query: string): Item[] { return items.filter(item => item.name.includes(query)); } } // Impure pipe — re-runs on every change detection cycle: @Pipe({ name: 'filterImpure', pure: false, standalone: true }) export class FilterImpurePipe implements PipeTransform { transform(items: Item[], query: string): Item[] { return items.filter(item => item.name.includes(query)); } }- A.Impure pipes are faster because they don't check references
- B.pure: false is required for pipes that return Observables
- C.Pure pipes are cached — they only re-run when the input's reference changes (an optimization). Impure pipes run on every change-detection cycle — necessary for mutated arrays/objects but expensive
- D.Pure pipes don't work with multiple arguments
Show answer
Answer: C. Pure pipes are cached — they only re-run when the input's reference changes (an optimization). Impure pipes run on every change-detection cycle — necessary for mutated arrays/objects but expensive
---From the quiz Advanced Angular
22.How do you create a custom structural directive like
*appUnless?javascript@Directive({ selector: '[appUnless]', standalone: true, }) export class UnlessDirective { private hasView = false; constructor( private templateRef: TemplateRef<unknown>, private viewContainer: ViewContainerRef ) {} @Input() set appUnless(condition: boolean) { if (!condition && !this.hasView) { this.viewContainer.createEmbeddedView(this.templateRef); this.hasView = true; } else if (condition && this.hasView) { this.viewContainer.clear(); this.hasView = false; } } } // Usage: <div *appUnless="isLoggedIn">Please sign in</div>- A.By extending NgIf and overriding its condition logic
- B.By using @HostBinding with the hidden attribute
- C.By injecting TemplateRef and ViewContainerRef, then calling createEmbeddedView or clear
- D.By returning null from the template expression
Show answer
Answer: C. By injecting TemplateRef and ViewContainerRef, then calling createEmbeddedView or clear
Structural directives useTemplateRef(the template to stamp) andViewContainerRef(where to stamp it).From the quiz Dynamic Components
23.What pattern enables Angular "composable" functions similar to React hooks?
javascript// composables/use-current-user.ts export function useCurrentUser(): Signal<User | null> { const authService = inject(AuthService); const userService = inject(UserService); return computed(() => { const token = authService.token(); return token ? userService.getUserFromToken(token) : null; }); } // composables/use-page-title.ts export function usePageTitle(title: string | Signal<string>): void { const titleService = inject(Title); const appName = inject(APP_NAME); effect(() => { const resolvedTitle = typeof title === 'string' ? title : title(); titleService.setTitle(`${resolvedTitle} | ${appName}`); }); } // Usage in component @Component({ ... }) export class ProfileComponent { // Called during field initialization = injection context ✅ currentUser = useCurrentUser(); constructor() { usePageTitle('Profile'); } }- A.Extending Injectable with a use prefix naming convention
- B.Using @Directive with exportAs to expose service instances
- C.Writing functions that call inject() and can be called during component/directive construction (injection context)
- D.Creating wrapper classes with static getInstance() methods
Show answer
Answer: C. Writing functions that call inject() and can be called during component/directive construction (injection context)
Functions that callinject()internally must be called during an injection context (constructor or field initializer). This enables powerful composable patterns:From the quiz Standalone & Tokens
24.How do you implement optimistic updates with NgRx?
javascript// Actions export const updateTodo = createAction('[Todo] Update Todo', props<{ todo: Todo }>()); export const updateTodoSuccess = createAction('[Todo API] Update Todo Success', props<{ todo: Todo }>()); export const updateTodoFailure = createAction('[Todo API] Update Todo Failure', props<{ originalTodo: Todo; error: string }>()); // Reducer — apply immediately on(updateTodo, (state, { todo }) => todoAdapter.upsertOne(todo, state) // ← optimistic update ), on(updateTodoSuccess, (state, { todo }) => todoAdapter.upsertOne(todo, state) // confirm with server version ), on(updateTodoFailure, (state, { originalTodo }) => todoAdapter.upsertOne(originalTodo, state) // revert to original ), // Effect — capture originalTodo (pre-optimistic value) for rollback. // concatLatestFrom reads the store lazily, when the action arrives. updateTodo$ = createEffect(() => this.actions$.pipe( ofType(updateTodo), concatLatestFrom(({ todo }) => this.store.select(selectTodoById(todo.id))), concatMap(([{ todo }, originalTodo]) => this.todoService.update(todo).pipe( map(updated => updateTodoSuccess({ todo: updated })), catchError(error => of(updateTodoFailure({ originalTodo: originalTodo!, error: error.message })) ), ) ), ) );- A.Wait for the API response before dispatching any state changes
- B.Update the store immediately on dispatch, and revert on API failure
- C.Use @ngrx/entity adapter's optimistic mode
- D.Dispatch a pending action and block UI until confirmation
Show answer
Answer: B. Update the store immediately on dispatch, and revert on API failure
Optimistic updates improve perceived performance by updating the UI before the API confirms:From the quiz Component Store
25.What does the
RouterStateSnapshotcontain?javascriptexport const loggingGuard: CanActivateFn = (route, state) => { // state: RouterStateSnapshot console.log('Navigating to:', state.url); // Traverse the route tree: let node: ActivatedRouteSnapshot | null = state.root; while (node) { console.log({ path: node.routeConfig?.path, params: node.params, queryParams: node.queryParams, data: node.data, }); node = node.firstChild; } return true; }; // Getting current state in a service: @Injectable({ providedIn: 'root' }) class RouteStateService { private router = inject(Router); getCurrentParams(): Record<string, string> { let route = this.router.routerState.snapshot.root; const params: Record<string, string> = {}; while (route.firstChild) { Object.assign(params, route.params); route = route.firstChild; } return { ...params, ...route.params }; } }- A.Only the current URL as a string
- B.A tree of ActivatedRouteSnapshot nodes representing the full current router state (all active routes, their params, data, and segments)
- C.A history of previous navigation events
- D.The resolved data for all active routes combined into one flat object
Show answer
Answer: B. A tree of ActivatedRouteSnapshot nodes representing the full current router state (all active routes, their params, data, and segments)
RouterStateSnapshotis a snapshot of the full router state at a point in time:From the quiz Functional Guards
26.What is the difference between
bufferTimeandwindowTime?javascriptimport { interval } from 'rxjs'; import { bufferTime, windowTime, mergeAll, map } from 'rxjs/operators'; // bufferTime — collects into arrays and emits arrays interval(100).pipe( bufferTime(500), // collect emissions for 500ms, then emit array ).subscribe(batch => { console.log('Batch:', batch); // [0,1,2,3,4] (5 values per 500ms) }); // windowTime — emits Observables (windows) interval(100).pipe( windowTime(500), // emit a new Observable every 500ms mergeMap(window$ => window$.pipe( reduce((acc, v) => acc + v, 0), // sum each window )), ).subscribe(windowSum => { console.log('Window sum:', windowSum); }); // bufferTime for batching API calls (debounce + batch pattern): userActions$.pipe( bufferTime(200), filter(actions => actions.length > 0), switchMap(actions => this.api.batchProcess(actions)), ).subscribe();- A.bufferTime uses fixed windows; windowTime uses sliding windows
- B.bufferTime collects emissions into arrays; windowTime groups them into sub-Observables
- C.windowTime is for time-based filtering; bufferTime is for count-based batching
- D.They are identical — windowTime is the newer API
Show answer
Answer: B. bufferTime collects emissions into arrays; windowTime groups them into sub-Observables
Both batch emissions over time, but differently:From the quiz Higher-Order Observables
27.What are
ng-containerandng-templateused for?javascript@Component({ template: ` <!-- ng-container: grouping without extra DOM element --> <ng-container *ngIf="isAdmin"> <button>Edit</button> <button>Delete</button> </ng-container> <!-- ng-template: reusable template block --> <ng-template #loadingTpl> <div class="spinner">Loading…</div> </ng-template> @if (isLoading) { <ng-container *ngTemplateOutlet="loadingTpl" /> } ` })- A.ng-container is a grouping element that adds no node to the real DOM — useful for applying directives across several elements. ng-template defines a reusable template block that is not rendered by default
- B.ng-container creates an invisible <div> in the DOM
- C.ng-template and ng-container are interchangeable
- D.ngTemplateOutlet requires CommonModule even in standalone components
Show answer
Answer: A. ng-container is a grouping element that adds no node to the real DOM — useful for applying directives across several elements. ng-template defines a reusable template block that is not rendered by default
---From the quiz Advanced Angular
28.What happens to a component's change detection when it uses a
signal()as its state?javascript@Component({ selector: 'app-counter', template: `<p>Count: {{ count() }}</p><button (click)="inc()">+1</button>`, standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, }) export class CounterComponent { count = signal(0); inc() { this.count.update(c => c + 1); } }- A.Only the components that read the signal are scheduled for re-render when the signal changes
- B.Change detection runs on every signal write, globally
- C.Signals bypass Angular's change detection entirely
- D.The component must be manually subscribed to the signal using effect()
Show answer
Answer: A. Only the components that read the signal are scheduled for re-render when the signal changes
Angular Signals introduce fine-grained reactivity. When a component template reads a signal, Angular tracks that dependency. When the signal changes, only that component (and its ancestors up to the firstOnPushboundary) is marked dirty and re-rendered. With signals andOnPush, Angular can skip entire subtrees of the component tree that haven't changed, enabling very efficient rendering.From the quiz Dynamic Components
29.What is
DestroyRefand how does it improve overngOnDestroy?javascript// utility function — no need for the consumer to manage cleanup export function autoRefresh(intervalMs: number): void { const destroyRef = inject(DestroyRef); const router = inject(Router); const id = setInterval(() => router.navigate(['/refresh']), intervalMs); destroyRef.onDestroy(() => clearInterval(id)); } // Service — cleanup tied to the injector's lifetime @Injectable() class WebSocketService { private ws: WebSocket; constructor(destroyRef: DestroyRef) { this.ws = new WebSocket('wss://api.example.com'); destroyRef.onDestroy(() => this.ws.close()); } } // takeUntilDestroyed operator uses DestroyRef internally import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ ... }) class FeedComponent { private feedService = inject(FeedService); ngOnInit() { this.feedService.getFeed() .pipe(takeUntilDestroyed()) // auto-completes when component is destroyed .subscribe(items => this.items = items); } }- A.A built-in service that automatically destroys all component dependencies
- B.A replacement for ChangeDetectorRef that controls component destruction timing
- C.An injectable that allows any code (not just components implementing OnDestroy) to register cleanup callbacks tied to a component or injector's lifecycle
- D.A token used to signal the destruction of lazy-loaded modules
Show answer
Answer: C. An injectable that allows any code (not just components implementing OnDestroy) to register cleanup callbacks tied to a component or injector's lifecycle
DestroyRef(Angular 16+) decouples cleanup logic fromngOnDestroy, enabling composable cleanup in services, utilities, and functions.From the quiz Standalone & Tokens
30.What is a meta-reducer in NgRx?
javascriptimport { MetaReducer, ActionReducer } from '@ngrx/store'; import { localStorageSync } from 'ngrx-store-localstorage'; // Meta-reducer 1: Logging function loggerMetaReducer(reducer: ActionReducer<any>): ActionReducer<any> { return (state, action) => { console.group(action.type); console.log('%c prev state', 'color: #9E9E9E', state); console.log('%c action', 'color: #03A9F4', action); const nextState = reducer(state, action); console.log('%c next state', 'color: #4CAF50', nextState); console.groupEnd(); return nextState; }; } // Meta-reducer 2: State hydration from localStorage function hydrationMetaReducer(reducer: ActionReducer<AppState>): ActionReducer<AppState> { return (state, action) => { if (action.type === INIT || action.type === UPDATE) { const storageValue = localStorage.getItem('app_state'); if (storageValue) { try { return JSON.parse(storageValue); } catch { /* invalid JSON */ } } } const nextState = reducer(state, action); localStorage.setItem('app_state', JSON.stringify(nextState)); return nextState; }; } // Register in provideStore: export const metaReducers: MetaReducer<AppState>[] = isDevMode() ? [loggerMetaReducer, hydrationMetaReducer] : [hydrationMetaReducer]; provideStore(reducers, { metaReducers })- A.A higher-order function that wraps a reducer with additional behavior — called before the reducer, able to transform actions or state
- B.A reducer that manages state for multiple features simultaneously
- C.A reducer factory that generates CRUD reducers automatically
- D.A NgRx Effect that doubles as a reducer
Show answer
Answer: A. A higher-order function that wraps a reducer with additional behavior — called before the reducer, able to transform actions or state
Meta-reducers wrap reducers to add cross-cutting concerns:From the quiz Component Store
Practise 650+ more Angular questions
Timed quizzes with instant feedback, the questions you miss coming back until they stick, coding challenges and AI mock interviews — free to start.