Angular JS Interview Questions:Intermediate Level

Signals, Standalone Components, NgRx, Routing & Interceptors, Advanced Forms, Signal Inputs, RxJS Mastery, Performance, and Security

Angular JS Signals, Standalone Components, NgRx, Routing & Interceptors

Q01
What are Angular Signals and how do they differ from RxJS BehaviorSubjects?

Signals are a synchronous, reactive primitive introduced in Angular 16. A Signal holds a value and automatically tracks dependencies when its value is read within a reactive context (like a template or an effect). When the value changes, it precisely notifies Angular’s change detector, allowing for fine-grained reactivity without Zone.js.

Unlike BehaviorSubject in RxJS, Signals do not require subscriptions or manual unsubscriptions (avoiding memory leaks), are inherently glitch-free (synchronous resolution), and don’t require the async pipe in templates. RxJS remains superior for asynchronous event streams, while Signals are optimized for synchronous application state.

import { signal, computed } from '@angular/core';

const count = signal(0);
const double = computed(() => count() * 2);

count.update(v => v + 1); // double automatically updates to 2
Q02
Explain the difference between a WritableSignal and a Computed Signal.

A WritableSignal (created using signal()) allows you to directly mutate its value using the .set() or .update() methods. It is the source of truth for a piece of state.

A Computed signal derives its value from other signals. It is read-only; you cannot call .set() on it. It is heavily memoized, meaning the computation function only runs when its dependencies change, and the result is cached until the next dependency update. This makes it perfect for expensive data transformations.

Q03
What is an effect() in Angular Signals and when should you use it?

An effect() is an operation that runs whenever one or more signal dependencies change. Angular automatically tracks any signal read inside the effect block. Effects are used exclusively for side effects—such as syncing data to localStorage, manipulating the DOM manually, or triggering analytics—not for updating other signals (which can cause infinite loops).

export class ThemeComponent {
  theme = signal('dark');

  constructor() {
    effect(() => {
      // Runs automatically whenever 'theme' signal changes
      localStorage.setItem('app-theme', this.theme());
    });
  }
}
Q04
How do you read a Signal inside an effect() without triggering a dependency track?

You use the untracked() function. If an effect depends on Signal A to trigger execution, but needs to read the current value of Signal B without re-executing when Signal B changes, you wrap the read of Signal B in untracked().

effect(() => {
  const user = this.currentUser(); // Triggers effect when user changes
  // Untracked read: Changing logLevel will NOT trigger this effect
  console.log(`User changed:`, user, untracked(this.logLevel)); 
});
Q05
What is a Standalone Component and how does it replace NgModules?

Introduced in Angular 14, a Standalone Component (marked with standalone: true) does not need to be declared in any @NgModule. Instead, it manages its own dependencies directly via its imports array. This drastically reduces boilerplate, flattens the learning curve, and enables better tree-shaking by Webpack/Esbuild, making the application lighter and faster.

@Component({
  selector: 'app-user',
  standalone: true,
  imports: [CommonModule, RouterModule], // Direct imports
  template: '<h1 *ngIf="active">User</h1>'
})
export class UserComponent {}
Q06
How do you bootstrap an Angular application without an AppModule?

In a standalone architecture, you bootstrap the application directly using a standalone root component via the bootstrapApplication function in main.ts. Global providers (like Router or HttpClient) are passed using the providers array in the configuration object.

import { bootstrapApplication } from '@angular/platform-browser';
import { provideRouter } from '@angular/router';

bootstrapApplication(AppComponent, {
  providers: [
    provideRouter(ROUTES),
    provideHttpClient()
  ]
}).catch(err => console.error(err));
Q07
How do you lazy load a Standalone Component in the Angular Router?

Instead of using loadChildren to load an NgModule, you use loadComponent and point it directly to the standalone component file. The router resolves the promise and instantiates the component without needing a module wrapper.

export const routes: Routes = [
  {
    path: 'dashboard',
    loadComponent: () => import('./dashboard.component').then(m => m.DashboardComponent)
  }
];
Q08
What are Functional Route Guards and why are they preferred over class-based guards?

Functional Route Guards (introduced in Angular 15) replace class-based implements of CanActivate or CanMatch. They are simple TypeScript functions that utilize the inject() function to access services. They eliminate class boilerplate, are easily composable, and can be defined inline directly in the route configuration.

export const authGuard: CanActivateFn = (route, state) => {
  const authService = inject(AuthService);
  const router = inject(Router);
  return authService.isLoggedIn() ? true : router.parseUrl('/login');
};
Q09
What is the difference between canLoad and canMatch in Angular Routing?

canLoad (now deprecated) prevented the browser from downloading a lazy-loaded chunk if the guard returned false, but it couldn’t fall back to another route with the same path. canMatch is the modern replacement. It evaluates before the chunk is downloaded, but if it returns false, the router will continue checking the route configuration array to see if a subsequent route matches the path, enabling advanced A/B testing or role-based routing structures.

Q10
How does a Route Resolver work and how do you implement a functional resolver?

A Resolver executes an asynchronous task (like fetching data) before the router transitions to the target component. The component only renders once the resolver’s Promise/Observable completes. Modern Angular uses functional resolvers via ResolveFn.

export const userResolver: ResolveFn<User> = (route) => {
  const userId = route.paramMap.get('id')!;
  return inject(UserService).getUserById(userId);
};

// Route Config
{ path: 'user/:id', component: UserComp, resolve: { user: userResolver } }
Q11
Explain the shift from HttpClientModule to provideHttpClient().

In a standalone application, importing HttpClientModule is an anti-pattern. Instead, Angular provides the provideHttpClient() function. It configures the DI system with the necessary HTTP services at the application root without the overhead of an NgModule. You can also append features like withInterceptors() directly inside the function call.

Q12
How do you create and register a Functional HTTP Interceptor?

Functional interceptors are simpler than class-based ones. They are pure functions that take the HttpRequest and a HttpHandlerFn (next), allowing you to clone and modify the request before passing it down the chain.

export const tokenInterceptor: HttpInterceptorFn = (req, next) => {
  const token = inject(AuthService).getToken();
  const cloned = req.clone({ setHeaders: { Authorization: `Bearer ${token}` } });
  return next(cloned);
};

// In app.config.ts
provideHttpClient(withInterceptors([tokenInterceptor]));
Q13
What is HttpContext and how is it used with Interceptors?

HttpContext allows you to pass custom metadata directly to HTTP Interceptors without modifying the HTTP headers (which the server would see). For example, you can create a token bypassing interceptor: if a request has BYPASS_AUTH set to true in its context, the interceptor checks this via req.context.get() and skips adding the JWT token.

Q14
Explain the unidirectional data flow in NgRx.

NgRx relies on a strict flow: The Component dispatches an Action. If an asynchronous task is needed (like HTTP), an Effect intercepts the action, performs the task, and dispatches a new Success/Failure Action. The Reducer catches the action, takes the old state, applies the payload, and returns a new immutable state. Finally, the Component reads the new state via Selectors, updating the UI.

Q15
Why are NgRx Selectors essential and what is memoization?

Selectors (createSelector) are pure functions used to extract slices of state from the store. They are essential because they provide memoization. If the store updates but the specific slice the selector listens to hasn’t changed, the selector returns the cached result without recalculating or triggering a component re-render. This prevents massive performance bottlenecks in large applications.

Q16
What is the purpose of NgRx Effects? Provide a functional example.

Effects isolate side effects (HTTP calls, WebSocket streams, logging) from components. They listen to the action stream, perform the side effect, and return a new action to the reducer. Modern NgRx uses functional effects via createEffect().

export const loadUsers = createEffect(
  (actions$ = inject(Actions), api = inject(UserService)) => {
    return actions$.pipe(
      ofType(UserActions.loadUsers),
      switchMap(() => api.getAll().pipe(
        map(users => UserActions.loadSuccess({ users })),
        catchError(error => of(UserActions.loadFailure({ error })))
      ))
    );
  },
  { functional: true }
);
Q17
When would you choose NgRx ComponentStore over the Global NgRx Store?

The Global Store (@ngrx/store) is for state shared across the entire application (e.g., auth tokens, user profiles). ComponentStore is a localized state management solution designed for specific component trees (like a complex multi-step wizard or a data grid). It binds its lifecycle to the component; when the component unmounts, the state is automatically garbage collected, preventing state pollution and memory leaks.

Q18
How does NgRx SignalStore differ from standard NgRx?

@ngrx/signals is the modern, lightweight alternative to RxJS-based NgRx. It leverages Angular Signals to manage state synchronously. It completely removes the boilerplate of Actions and Reducers, opting for a functional, patch-based state update approach using patchState() while keeping RxJS strictly for asynchronous effects via rxMethod.

Q19
Explain the difference between switchMap, mergeMap, and concatMap.

These are RxJS flattening operators used heavily in Angular HTTP requests:

  • switchMap: Cancels the previous inner observable if a new emission arrives. Perfect for Search auto-complete (cancels old HTTP requests).
  • mergeMap: Processes all emissions in parallel without cancelling. Good for independent background saves.
  • concatMap: Queues emissions strictly in order. The second request won’t start until the first finishes. Good for sequential database inserts.
Q20
How does takeUntilDestroyed() solve memory leaks?

Historically, developers had to implement ngOnDestroy and use a Subject to complete component-level RxJS subscriptions. Angular 16 introduced takeUntilDestroyed(), an operator that automatically ties the subscription to the current Injection Context (the component’s lifecycle). When the component unmounts, it automatically unsubscribes the observable pipeline.

export class SearchComp {
  constructor() {
    this.searchCtrl.valueChanges.pipe(
      takeUntilDestroyed() // Automatically unsubscribes on destroy
    ).subscribe(val => console.log(val));
  }
}
Q21
What is the difference between a Subject and a BehaviorSubject?

A Subject acts purely as an event emitter; if a component subscribes to it *after* it has emitted a value, the component misses that value. A BehaviorSubject requires an initial value upon instantiation and caches the *latest* emitted value. Any late subscriber immediately receives this cached value upon subscription. It is the core building block for standard Angular state services.

Q22
Why should you use shareReplay() when caching HTTP data?

If you assign an httpClient.get() observable to a variable and use the async pipe multiple times in the template, Angular will execute a distinct HTTP network request for every single pipe. Appending shareReplay({ bufferSize: 1, refCount: true }) multicasts the stream. The first subscriber triggers the HTTP call, and subsequent subscribers instantly receive the cached response without triggering extra network requests.

Q23
What is forkJoin and when is it appropriate to use?

forkJoin is an RxJS creation operator that takes an array or dictionary of Observables, waits for all of them to successfully complete, and then emits a single array/object containing all the final values. It is ideal for Dashboard initialization where you must fetch data from 3 different APIs in parallel before rendering the page.

Q24
Explain the purpose of distinctUntilChanged().

This operator filters out consecutive identical emissions. If a source stream emits [1, 1, 2, 2, 1], distinctUntilChanged() will output [1, 2, 1]. In Angular, it is used on form valueChanges or NgRx selections to prevent components from re-rendering when the underlying data hasn’t physically changed.

Q25
How do you manage an unknown number of dynamic inputs using Reactive Forms?

You use a FormArray. Unlike a FormGroup which uses named keys, a FormArray manages an indexed array of FormControl, FormGroup, or other FormArray instances. It allows you to dynamically push() new controls or removeAt() existing ones based on user interactions (like an “Add Telephone Number” button).

this.form = this.fb.group({
  phones: this.fb.array([ this.fb.control('') ])
});

addPhone() {
  this.phones.push(this.fb.control(''));
}
Q26
How do you implement Cross-Field Validation in Reactive Forms?

To validate two fields against each other (e.g., ‘Password’ and ‘Confirm Password’), you cannot attach the validator to the individual FormControl. Instead, you attach a custom validator to their parent FormGroup. The validator function accesses the group, reads both child controls, and returns an error object if they don’t match.

Q27
What is an Async Validator and how does it differ from a standard Validator?

While a standard validator returns a validation object synchronously, an Async Validator returns a Promise or an Observable. It is used when validation requires a backend check, such as querying an API to see if a chosen “Username” is already taken. Angular automatically manages the PENDING state of the control while waiting for the response.

Q28
Explain the role of the ControlValueAccessor (CVA) interface.

The CVA interface acts as a bridge between Angular’s Forms API and a custom DOM element. If you build a complex custom component (like a star-rating widget), implementing CVA ensures it works natively with formControlName or [(ngModel)]. You must implement methods like writeValue (data to DOM) and registerOnChange (DOM to data).

Q29
How do you react to form status changes?

Every AbstractControl (FormGroup, FormControl) provides a statusChanges Observable. By subscribing to it, you can trigger logic whenever the form transitions between VALID, INVALID, PENDING, or DISABLED. This is useful for disabling a submit button dynamically or showing global error messages.

Q30
Explain ChangeDetectionStrategy.OnPush and why it improves performance.

By default, Angular checks every component in the tree during a change detection cycle. Setting OnPush tells Angular to skip checking this component unless: 1) An @Input reference physically changes, 2) An event originates from the component itself, or 3) An async pipe receives a new emission. This drastically reduces CPU overhead in complex grids and lists by halting unnecessary re-renders.

Q31
What is the purpose of the modern @for control flow over *ngFor?

Introduced in Angular 17, @for is a built-in control flow syntax replacing the *ngFor structural directive. It is significantly faster because it operates at the compiler level rather than as a directive, reduces bundle size, and forces the developer to provide a track expression by default, preventing the classic DOM-recreation performance bugs associated with missing trackBy functions.

<!-- Modern @for syntax -->
@for (item of items; track item.id) {
  <li>{{ item.name }}</li>
} @empty {
  <li>No items found.</li>
}
Q32
What is the difference between @ViewChild and @ContentChild?

@ViewChild queries elements or components located entirely within the component’s own HTML template. @ContentChild queries elements that are projected into the component from a parent using <ng-content>. View queries resolve in ngAfterViewInit, while Content queries resolve earlier in ngAfterContentInit.

Q33
Explain Multi-Slot Content Projection.

Instead of projecting all parent content into a single <ng-content> tag, a component can define multiple slots using the select attribute (targeting CSS classes, attributes, or elements). This allows a component like a Card to neatly distribute headers, bodies, and footers into specific DOM locations.

<!-- Card Component Template -->
<div class="header"><ng-content select="[card-header]"></ng-content></div>
<div class="body"><ng-content></ng-content></div>
Q34
What is ngTemplateOutlet used for?

ngTemplateOutlet is a structural directive used to instantiate a template (<ng-template>) dynamically and insert it into the DOM. It is heavily used in generic components (like a Data Table) to allow the parent component to pass down customized HTML templates for rendering specific table cells, complete with a context object.

Q35
What is the difference between @HostListener and @HostBinding?

Used primarily in Attribute Directives: @HostListener listens to DOM events on the host element (like ‘mouseenter’ or ‘click’) and triggers a method. @HostBinding binds a class property to a property of the host element (like class.active or style.color). They provide a safe way to interact with the host element without direct DOM manipulation.

Q36
Why should you use Renderer2 instead of native DOM methods like document.getElementById?

Angular is designed to be platform-agnostic (it can run in a browser, on a server via Node.js for SSR, or inside a Web Worker). Direct references to the document or window object will crash the app during Server-Side Rendering because those APIs don’t exist in Node.js. Renderer2 provides an abstraction layer to safely manipulate elements across all platforms.

Q37
What is APP_INITIALIZER?

APP_INITIALIZER is a multi-provider DI token that executes functions when the Angular app boots. The framework delays the initialization of the application until all Promises/Observables provided by the initializers complete. It is crucial for fetching essential runtime configurations (like environment variables from an API) before the UI renders.

Q38
Explain the difference between @Self(), @SkipSelf(), and @Host() in Dependency Injection.

These resolution modifiers restrict how Angular’s DI looks for a service:

  • @Self: Looks *only* in the component’s own providers. Throws an error if not found.
  • @SkipSelf: Bypasses the component’s own providers and starts looking in the parent component.
  • @Host: Looks up the tree but stops at the host component (useful in directives ensuring they don’t reach global scope).
Q39
What is View Encapsulation?

Angular’s View Encapsulation dictates how CSS styles apply to components. By default (Emulated), Angular dynamically assigns attributes to elements and modifies the component’s CSS so that styles do not leak out into other components. Changing it to None makes the CSS global, and ShadowDom uses the browser’s native Shadow DOM API for strict encapsulation.

Q40
How do you handle global errors in Angular?

By default, unhandled exceptions print to the console. You can intercept them by creating a custom class that implements the ErrorHandler interface, overriding the handleError(error) method. You then provide this class at the application root. Inside the handler, you format the error and send it to a telemetry service (like Sentry or Datadog) for centralized monitoring.

Q41
What are Angular Router Events?

The Router exposes an events Observable. By subscribing to it, you can hook into the navigation lifecycle. Events like NavigationStart, RoutesRecognized, and NavigationEnd are frequently used to show/hide global loading spinners, log analytics, or reset scroll positions on page transitions.

Q42
What is the purpose of the Title and Meta services?

For applications needing SEO (especially those using Angular Universal/SSR), dynamically updating the page title and meta descriptions on route changes is critical. Injecting the Title and Meta services provided by @angular/platform-browser allows you to programmatically modify the document’s <head> tags from within your components.

Q43
What is Non-Destructive Hydration in Angular Server-Side Rendering (SSR)?

Historically, Angular SSR would render HTML on the server, but when the client loaded, Angular would destroy the DOM and rebuild it from scratch, causing screen flicker. Introduced in Angular 16, Non-Destructive Hydration reuses the server-rendered DOM nodes. It merely attaches event listeners to existing elements, massively improving Core Web Vitals (LCP and CLS).

Q44
Explain the inject() function and why it is replacing constructor injection.

The inject(Token) function allows dependency injection to happen outside of a class constructor (but strictly within an injection context). It is the foundation for modern Angular features like functional guards, interceptors, and signals. It heavily reduces boilerplate in components relying on base classes, as child components no longer need to pass dependencies up via super().

Q45
What is NgZone and why would you use runOutsideAngular?

Angular relies on Zone.js to monkey-patch asynchronous browser events (like setTimeout or clicks) to automatically trigger change detection. If you have a heavy operation (like a requestAnimationFrame loop or a noisy WebSocket), it will trigger change detection constantly, freezing the app. Using this.ngZone.runOutsideAngular(() => { ... }) executes the code without notifying Angular, preserving performance.

Q46
How do you handle immutable HTTP parameters using HttpParams?

The HttpParams object in Angular is immutable. If you attempt to add parameters like params.set('id', '1'), it returns a new instance rather than modifying the existing one. You must reassign the result to chain parameters correctly.

let params = new HttpParams();
params = params.set('page', '1').set('sort', 'asc');
this.http.get('/api/data', { params });
Q47
What is deferrable views (@defer) introduced in Angular 17?

@defer allows declarative, highly granular lazy loading of components directly inside templates without complex routing configurations. You can wrap a heavy charting component in a @defer (on viewport) block. Angular will extract that component into a separate JavaScript chunk during the build, and only download/render it when the user scrolls it into view.

Q48
How does Angular’s forwardRef() function work?

In TypeScript, classes cannot be referenced before they are defined. If Component A needs to inject Service B, but Service B is declared later in the file (or involves circular dependencies), Angular throws an error. forwardRef(() => ServiceB) creates an indirect reference that Angular resolves later at runtime, breaking the circular dependency chain safely.

Q49
What are standard ViewProviders vs normal Providers?

providers defined on a Component are available to the Component itself, its view, and any projected content (via <ng-content>). viewProviders restrict the visibility of the service strictly to the component’s internal view template. Components projected in from the outside cannot access services provided in viewProviders, offering strict boundary encapsulation.

Q50
Explain the concept of Structural Directives microsyntax.

When you use an asterisk (like *ngIf), Angular expands this microsyntax into an <ng-template> under the hood. The directive physically manipulates this template. Understanding this expansion is critical when building custom structural directives, as you use TemplateRef and ViewContainerRef inside the directive class to programmatically embed or clear the view based on your custom logic.

Angular JS: Advanced Forms, Signal Inputs, RxJS Mastery, Performance, and Security

Q51
How do you optimize validation performance using the updateOn property in Reactive Forms?

By default, Angular runs validators on every single keystroke (updateOn: 'change'). For complex forms or async validators checking backend APIs, this causes severe performance degradation. You can configure a FormControl or FormGroup to only trigger validation when the input loses focus ('blur') or when the user submits the form ('submit').

this.usernameCtrl = new FormControl('', {
  updateOn: 'blur',
  validators: [Validators.required],
  asyncValidators: [this.uniqueUsernameValidator]
});
Q52
Explain how to write a custom synchronous validator in Angular.

A custom validator is a function that receives an AbstractControl and returns either a validation error object (if validation fails) or null (if validation passes). The key of the returned object is usually the name of the error, which you use in the template to show specific messages.

export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
  return (control: AbstractControl): ValidationErrors | null => {
    const forbidden = nameRe.test(control.value);
    return forbidden ? { forbiddenName: { value: control.value } } : null;
  };
}
Q53
What is the purpose of FormRecord in Angular 14+?

Before Angular 14, FormGroup was used for both strictly typed forms and highly dynamic forms with unknown keys. FormRecord was introduced as a specialized FormGroup where all controls must share the same type, but the keys are completely dynamic. It is ideal for scenarios like dynamic checkbox lists generated from a backend database.

// All dynamic keys will hold a FormControl strictly typed to boolean
const dynamicChecks = new FormRecord<FormControl<boolean>>({});
dynamicChecks.addControl('admin', new FormControl(true));
Q54
How do you handle dynamically adding/removing validations at runtime?

You use the setValidators() or clearValidators() methods on the specific FormControl. Crucially, after changing the validators, you must call updateValueAndValidity() to force Angular to recalculate the form’s validity state based on the new rules.

if (userType === 'company') {
  this.taxIdCtrl.setValidators([Validators.required]);
} else {
  this.taxIdCtrl.clearValidators();
}
this.taxIdCtrl.updateValueAndValidity();
Q55
What is the difference between patchValue() and setValue()?

setValue() strictly requires you to provide an object that exactly matches the structure of the FormGroup. If a key is missing or extra, it throws an error. patchValue() is more forgiving; it updates only the controls corresponding to the keys provided in the object, ignoring the rest. setValue() is preferred when strict data integrity is required.

Q56
Explain withComponentInputBinding() in the Angular Router.

Introduced in Angular 16, this router feature eliminates the need to manually inject ActivatedRoute to read parameters, query params, or route data. When enabled, the router automatically maps route parameters directly to the component’s @Input() or input() signal properties, vastly simplifying component code.

// App Config
provideRouter(routes, withComponentInputBinding());

// Component
@Component({...})
export class UserComp {
  // URL: /user/42 -> userId automatically becomes '42'
  @Input() userId!: string; 
}
Q57
How does the PreloadAllModules strategy work, and why might you implement a Custom Preloading Strategy?

PreloadAllModules tells the router to instantly download all lazy-loaded chunks in the background as soon as the main application finishes bootstrapping. While it speeds up subsequent navigation, it wastes bandwidth on massive enterprise apps. A Custom Preloading Strategy allows you to selectively preload chunks based on route data (e.g., data: { preload: true }) or based on the user’s network connection speed.

Q58
What is a TitleStrategy in Angular Routing?

Instead of manually injecting the Title service into every component, you can define a title property on the route configuration. By extending the built-in TitleStrategy class and overriding the updateTitle() method, you can create a centralized, globally managed title formatting system (e.g., appending “- My App Name” to every route title) that executes automatically on navigation.

Q59
How do you preserve Query Parameters when navigating?

By default, navigating to a new route strips away existing query parameters. To preserve them (useful for keeping search filters active when clicking into a detail view), you set queryParamsHandling: 'preserve' or 'merge' in the NavigationExtras object via router.navigate() or the routerLink directive.

Q60
How do you handle routing to an external URL outside of your Angular application?

The Angular Router is strictly for navigating the internal component tree. If you try to router.navigate(['https://google.com']), Angular will treat it as a relative local path. To navigate externally, you must bypass the router entirely and use standard DOM APIs like window.location.href = 'https://google.com'.

Q61
What is the difference between catchError and throwError in an HTTP pipeline?

catchError is an operator that intercepts a failed observable stream, allowing you to handle the error (like showing a toast notification). Inside catchError, you must return a new replacement observable. If you want the error to continue propagating down to the component’s .subscribe(error => ...) block, you use the throwError() creation function to re-throw it as a fresh observable error stream.

Q62
Explain why exhaustMap is crucial for login forms or submit buttons.

If a user double-clicks a “Submit Order” button, switchMap would cancel the first request and fire a second (risking backend anomalies), while mergeMap would fire both simultaneously (duplicate orders). exhaustMap ignores all subsequent emissions until the current active inner observable completes. This makes it the absolute safest operator for preventing accidental double-submissions.

Q63
What is the difference between combineLatest and withLatestFrom?

combineLatest takes an array of observables and emits an array of their latest values whenever any of them emit a new value (after all have emitted at least once). withLatestFrom is used when you only want to trigger an emission when the primary source observable emits, simply “pulling in” the most recent value from the secondary observable without letting the secondary observable trigger the pipeline itself.

Q64
How do you execute logic regardless of whether an HTTP request succeeds or fails?

Instead of duplicating code in both the next and error blocks of a subscription, you use the RxJS finalize() operator in the pipe. It executes a callback function when the observable stream completely terminates (either by successfully completing or by erroring out). This is the standard pattern for turning off loading spinners.

this.http.get('/data').pipe(
  finalize(() => this.isLoading = false)
).subscribe(...);
Q65
What causes an RxJS memory leak, and how does shareReplay({refCount: true}) mitigate it?

A memory leak occurs when a component is destroyed, but its subscription to a long-lived observable (like a global service or interval) remains active, trapping the component in memory. shareReplay(1) multicasts the stream but keeps the connection alive permanently. Adding refCount: true tells the observable to automatically tear itself down and disconnect from the source when the number of active subscribers drops to zero.

Q66
What are Signal Inputs (input()) and why are they superior to @Input()?

Introduced in Angular 17.1, input() provides a reactive alternative to the @Input() decorator. Instead of relying on ngOnChanges to detect property updates, the input itself is a strictly typed Signal. This allows you to effortlessly derive state using computed() based directly on the input, ensuring perfect reactivity without lifecycle hook spaghetti.

export class ProductComp {
  // Replaces @Input() productId: string;
  productId = input.required<string>(); 
  
  // Automatically recalculates when productId changes
  isFeatured = computed(() => this.productId() === '123');
}
Q67
Explain Model Inputs (model()) for two-way binding.

Historically, two-way binding required a paired @Input() value and @Output() valueChange. The new model() function defines a writable signal that automatically acts as both the input and the event emitter. When the child component calls this.myModel.set(newValue), Angular automatically emits the change back to the parent, streamlining custom two-way bound components.

Q68
How do you convert an RxJS Observable into an Angular Signal?

You use the toSignal() utility function from @angular/core/rxjs-interop. It subscribes to the observable under the hood and updates the signal’s value upon emission. Crucially, it automatically unsubscribes when the injection context (the component) is destroyed. Because signals must have an initial synchronous value, you either provide an initialValue or let it return undefined until the first emission.

Q69
How do you convert a Signal back into an RxJS Observable?

You use the toObservable() utility function. This is necessary when your synchronous Signal state needs to interface with asynchronous pipelines, like triggering an HTTP request when a Signal’s value changes. It creates an observable that emits whenever the signal’s value updates.

const query = signal('angular');
const query$ = toObservable(query);

query$.pipe(
  debounceTime(300),
  switchMap(q => api.search(q))
).subscribe();
Q70
What does it mean that Signals are “Glitch-Free”?

In standard RxJS, if Observable C depends on Observable A and Observable B, and A changes (which also updates B), C might emit twice in rapid succession (a “glitch”), evaluating an intermediate, inconsistent state. Signals are mathematically designed as a push/pull topological graph. Angular marks dependencies as “dirty” synchronously, but only “pulls” the recalculation once the framework confirms all upstream signals have settled, guaranteeing the UI never renders an intermediate, invalid state.

Q71
What is createFeature in modern NgRx?

createFeature is a modern, boilerplate-reducing API in the NgRx Global Store. It encapsulates the feature’s name, its reducer, and automatically generates default selectors for every top-level property in the state slice. This completely eliminates the need to manually write boilerplate createSelector functions for simple state properties.

Q72
Explain the role of the NgRx Entity Adapter.

Managing collections of items (like a list of users) in Redux can be tedious, especially when updating a specific item requires mapping over the entire array. @ngrx/entity provides an Entity Adapter that normalizes state into a dictionary map ({ ids: [], entities: {} }). It provides built-in reducer methods like addOne, updateOne, and removeMany, turning expensive O(n) array operations into highly efficient O(1) dictionary lookups.

Q73
How does ComponentStore.updater() differ from ComponentStore.effect()?

In NgRx ComponentStore, an updater is a pure, synchronous function that takes the current state and a value, returning a new immutable state (exactly like a classic Reducer). An effect is used to manage asynchronous side operations. It takes an observable of values, executes async tasks (like HTTP calls using switchMap), and typically calls an updater upon success to modify the state.

Q74
How do you handle Hydration/Rehydration in an NgRx application?

Hydration refers to loading state from an external source (like localStorage) into the NgRx store upon application startup. This is achieved using a Meta-Reducer. The Meta-Reducer acts as a higher-order reducer that intercepts the initialization action, reads from local storage, and merges the stored payload into the initial state tree before passing control back to the standard reducers.

Q75
Why should NgRx Actions be viewed as Events rather than Commands?

Architecturally, actions should describe *what happened* in the application (e.g., [Login Page] Submit Button Clicked), not *what the system should do* (e.g., [Auth] Login User). Treating actions as events decouples the sender from the receiver. Multiple reducers or effects can listen to a single “Event” action and react independently, whereas “Command” actions create tight coupling and brittle architectures.

Q76
What is the difference between ng-container and ng-template?

<ng-template> is a completely inert block of HTML. Angular will not render it to the DOM unless explicitly instructed to do so via ngTemplateOutlet or a structural directive. <ng-container> is a logical grouping element that is rendered immediately, but it does not add an extra physical DOM element (like a <div> would). It is used to apply structural directives (*ngIf, *ngFor) without bloating the DOM tree.

Q77
How does @ViewChildren differ from @ViewChild?

While @ViewChild grabs the first matching element/component in the template, @ViewChildren grabs a collection of all matching elements and returns them as a QueryList. The QueryList is an observable structure; if elements are dynamically added or removed via *ngIf or @for, you can subscribe to queryList.changes to react to the DOM updates dynamically.

Q78
Explain how ngOnChanges works and when to use an @Input setter instead.

ngOnChanges fires whenever Angular detects a change to any @Input property, passing a SimpleChanges object containing the old and new values. It’s useful when multiple inputs change simultaneously and you need to calculate state based on their combination. However, if you only care about a single input changing, an ES6 setter on the @Input property is much cleaner, as it executes localized logic specifically when that exact property receives a new value.

Q79
What is a ViewContainerRef and how is it used in dynamic component creation?

ViewContainerRef represents a container where one or more views can be attached. When creating components dynamically (not routed, but spawned via code like a Toast message), you inject ViewContainerRef and call its createComponent() method. This instantiates the component and explicitly inserts it into the DOM at the container’s anchor point.

Q80
How do you bypass interceptors for a specific HTTP request?

If you need to make an HTTP request that explicitly skips all configured interceptors (like calling an external unauthenticated third-party API where your JWT token would cause a CORS/Auth failure), you use the HttpBackend handler. By injecting HttpBackend and creating a new isolated HttpClient(backend), requests made from this client bypass the global interceptor chain entirely.

Q81
Explain the difference between markForCheck() and detectChanges() in ChangeDetectorRef.

When using OnPush, if state mutates asynchronously outside of Angular’s knowledge (like a WebSocket emission), the view won’t update. markForCheck() flags the component and all its ancestors as “dirty”; Angular will then naturally check them during the next scheduled change detection cycle. detectChanges() is aggressive; it synchronously and immediately forces a change detection run on the component and its children, regardless of the cycle.

Q82
What are Angular Web Workers and how do they communicate?

Web Workers run heavy computational JavaScript (like parsing massive CSVs or running cryptography) on a separate background thread, keeping the main UI thread unblocked and preventing frame drops. In Angular, you generate a worker via CLI (ng g web-worker). The component and the worker communicate strictly via asynchronous message passing (postMessage() and onmessage listeners).

Q83
What are Bundle Budgets in `angular.json`?

Bundle budgets are performance guardrails configured in the angular.json build settings. You can specify maximum sizes for the initial load, specific lazy chunks, or component styles. If a developer imports a massive library that pushes the bundle size beyond the ‘warning’ threshold, the CLI alerts them. If it breaches the ‘error’ threshold, the production build automatically fails, strictly preventing application bloat.

Q84
Explain the architectural benefit of the new Application Builder (esbuild) over Webpack.

Starting in Angular 17, the default build system switched from Webpack to an esbuild and Vite-powered Application Builder. Because esbuild is written in Go, it compiles code down to machine language, utilizing aggressive parallel processing. This results in build times and dev-server hot reloads that are exponentially faster (often 60-80% faster) than the JavaScript-based Webpack compiler, vastly improving developer productivity.

Q85
How do you resolve “ExpressionHasChangedAfterItWasChecked” errors?

This strictly development-mode error occurs when Angular detects that a bound value in the template changed *after* the change detection cycle had already verified it (usually caused by modifying state synchronously inside ngAfterViewInit). The fix is architectural: either move the state mutation to ngOnInit, defer the update using setTimeout() (or Promise.resolve()) to push it to the next macro-task queue, or refactor the logic to use observable streams/signals.

Q86
What is an InjectionToken and when is it necessary?

While classes can be injected directly (e.g., inject(UserService)), primitive values like strings, configuration objects, or interfaces do not have runtime representations in JavaScript. To inject a configuration object (like an API URL), you must instantiate an InjectionToken. This creates a unique memory reference that the DI system uses as a lookup key for providing and injecting the value.

export const API_URL = new InjectionToken<string>('API_URL');

// Provide
{ provide: API_URL, useValue: 'https://api.com' }

// Inject
const url = inject(API_URL);
Q87
Explain the difference between useClass, useValue, and useFactory in providers.
  • useClass: Instantiates a new instance of the specified class (great for overriding a default service with a mock version during testing).
  • useValue: Provides a static, pre-existing value, primitive, or object (like a config object).
  • useFactory: Executes a function to dynamically determine and return the dependency value at runtime based on logic or other injected dependencies.
Q88
What does multi: true do in a provider configuration?

Normally, if you provide the same injection token twice, the second definition overwrites the first. Using multi: true tells Angular’s DI system to aggregate all provided values into an array. This is the exact mechanism used to provide multiple HTTP Interceptors or APP_INITIALIZER functions; Angular resolves the single token and gets an array of interceptors to execute sequentially.

Q89
What is the exportAs property in an Angular Directive or Component?

The exportAs property allows a directive or component to expose its internal class instance directly to the HTML template as a template reference variable. This is heavily used in Template-Driven forms (#myForm="ngForm"), allowing developers to call component/directive methods directly from the HTML without needing @ViewChild in the TypeScript file.

Q90
Explain how HostBinding and HostListener interact with CSS and Events.

Inside a custom Directive applied to an element (like an <input>): @HostListener('focus') captures the focus event on the input, allowing you to run logic. @HostBinding('class.focused') binds a boolean class property to the input’s actual DOM class list. Combining them allows you to dynamically append CSS classes to host elements based purely on their event states without manipulating the DOM directly.

Q91
How does Angular protect against Cross-Site Scripting (XSS)?

Angular treats all values bound into the DOM via interpolation or property binding as untrusted by default. Before rendering, Angular runs the data through its DomSanitizer. The sanitizer physically inspects the string and strips out any potentially malicious tags (like <script>) or executable attributes (like javascript: URLs), rendering the input entirely inert and preventing XSS execution.

Q92
How do you deliberately bypass Angular’s security sanitization?

If you are injecting HTML that you explicitly trust (like an <iframe> URL retrieved from your own secure backend), you inject the DomSanitizer service and call specific methods like bypassSecurityTrustHtml() or bypassSecurityTrustResourceUrl(). This explicitly tells Angular’s compiler to skip scrubbing the value. This must be used with extreme caution, as it opens the application to XSS if the data is compromised.

Q93
What is HttpClientXsrfModule and how does it prevent CSRF attacks?

Cross-Site Request Forgery (CSRF) is mitigated using the Double Submit Cookie pattern. By importing HttpClientXsrfModule (or using provideHttpClient(withXsrfConfiguration())), Angular automatically looks for a specific cookie (usually XSRF-TOKEN) set by the backend. It reads this cookie and attaches its value as a custom header (X-XSRF-TOKEN) on all mutating requests (POST, PUT), ensuring the backend can verify the request originated from the legitimate client.

Q94
Explain the purpose of TestBed.configureTestingModule().

In Angular unit testing (Jasmine/Jest), a component relies on dependency injection. TestBed creates a dynamic, isolated Angular testing module environment for the component. configureTestingModule() is where you declare the component being tested and provide Mock versions of its required services or modules, ensuring you are testing the component in isolation, not its external dependencies.

Q95
How do you test HTTP requests using HttpTestingController?

When testing services, you do not want to make real network calls. By importing provideHttpClientTesting() into the TestBed, you gain access to the HttpTestingController. You execute the service method, then use the controller’s expectOne('api/url') method to assert the request was made, and call .flush(mockData) to simulate the backend returning a successful JSON response, resolving the observable synchronously.

Q96
What is the purpose of fakeAsync and tick() in unit tests?

Testing asynchronous code (like setTimeout or debounceTime) normally requires complex async/await blocks that slow down the test suite. Wrapping the test block in fakeAsync() creates a virtual clock. Calling tick(500) instantly fast-forwards this virtual clock by 500 milliseconds, allowing you to test time-based asynchronous logic completely synchronously and instantaneously.

Q97
How do you mock an injected Signal in a Component test?

Because WritableSignals are just functions equipped with a .set() method, mocking them in tests is straightforward. You can provide a mock service where the state is represented by a fresh signal(mockValue). During the test, you can simply call mockService.mySignal.set(newMockValue) and call fixture.detectChanges() to assert that the component reacted correctly to the signal update.

Q98
What is provideAnimationsAsync()?

Instead of eagerly loading the entire @angular/animations package at bootstrap, provideAnimationsAsync() defers the loading of the animation engine until a component containing an animation actually renders on the screen. This drastically reduces the initial JavaScript bundle payload, accelerating the application’s Initial Load Time and Time to Interactive.

Q99
How does Angular Elements integrate Angular with other frameworks?

Angular Elements packages standard Angular components as native Custom Elements (Web Components) adhering to browser standards. Using the createCustomElement() API, you compile an Angular component into a standalone, framework-agnostic HTML tag (e.g., <my-angular-widget>). This tag can then be natively embedded and executed inside a React, Vue, or vanilla JavaScript application, complete with Angular’s change detection running under the hood.

Q100
Explain the TransferState API in Angular Universal (SSR).

During Server-Side Rendering, the server makes API calls to build the HTML. When the HTML reaches the client, the client-side Angular app normally re-executes those exact same API calls during hydration, causing network duplication and UI flickering. The TransferState API allows the server to serialize the API responses into a JSON object embedded in the HTML. The client reads this embedded data instantly upon loading, entirely bypassing the duplicate HTTP requests.