130 Angular Interview Questions and Answers (2026)

Angular has quietly become the backbone of serious enterprise and AI-era frontends, which means more companies are hiring for it and interviewers now expect real fluency, not buzzwords. Standalone components, signals, and zoneless change detection have reset what "knowing Angular" means. Walk in shaky on those and a stronger candidate takes the offer.
These 130 questions come with concise, interview-ready answers and code where it actually helps. They're worked from Junior to Mid to Senior, so you build from fundamentals up to the deep internals. Study them in order and you'll walk in able to explain the why, not just the how.
Q1.What are standalone components, and why are they now the default over NgModules?
NgModules?Standalone components are components that declare their own dependencies via an imports array instead of being registered in an NgModule. They became the default (from Angular v17) because they remove the boilerplate and indirection of modules while keeping the same capabilities.
Self-contained: Marked standalone: true (now implicit); a component imports the pipes, directives, and other components it uses directly.
Less boilerplate: No need to declare a component in a module and re-export it just to share it; dependencies are explicit and local.
Easier to reason about: You see exactly what a component depends on by reading its imports, which also helps tooling and lazy loading.
Full feature parity: Standalone components support routing, lazy loading, and DI without any module wrapper.
Q2.Can you explain the difference between declarations, imports, and providers in the context of a component or module?
declarations, imports, and providers in the context of a component or module?They control three different things: declarations registers the components/directives/pipes an NgModule owns, imports brings in other modules (or standalone items) whose exported declarables you want to use, and providers registers injectable services with the DI system.
declarations:
Only in NgModule: lists the components, directives, and pipes that belong to that module. Each declarable belongs to exactly one module.
Standalone components do not use declarations at all.
imports:
Makes another module's exported declarables (or standalone components/pipes/directives) available in templates.
This is the array standalone components use to pull in their template dependencies.
providers:
Configures DI: tells Angular how to create a service and at what scope it lives.
Most root services instead use providedIn: 'root', avoiding manual registration.
Q3.What is the difference between the constructor and ngOnInit? Why shouldn't you fetch data in a constructor?
constructor and ngOnInit? Why shouldn't you fetch data in a constructor?The constructor is a TypeScript class feature used for dependency injection and simple field setup, while ngOnInit is an Angular lifecycle hook that runs after the component's inputs are set and it's fully initialized. You avoid data fetching in the constructor because inputs aren't bound yet and it couples construction to side effects.
constructor:
Called by JS when the class is instantiated; Angular uses it to inject dependencies.
Bound @Input() values are not yet available here.
ngOnInit:
Runs once after the first ngOnChanges, so inputs are resolved and it's safe to read them.
The right place for initialization logic and kicking off data loads.
Why not fetch in the constructor:
Inputs you'd base the request on may be undefined.
It mixes construction with side effects, making the component harder to test and to instantiate cleanly.
Q4.Describe the full lifecycle of a component from creation to destruction. Which hook is best for DOM manipulation?
A component lives through construction, input binding, view/content initialization, repeated change-detection checks, and finally destruction. For safe DOM manipulation use ngAfterViewInit, because the component's view and its children are fully rendered by then.
Creation: The constructor runs (dependency injection); avoid heavy logic here since inputs aren't set yet.
Input binding and init: ngOnChanges (if inputs exist), then ngOnInit once for setup.
Content and view setup: ngAfterContentInit for projected content, then ngAfterViewInit once the view exists.
Ongoing checks: Every change detection run fires ngDoCheck, ngAfterContentChecked, and ngAfterViewChecked.
Destruction: ngOnDestroy for cleanup (unsubscribe, detach listeners) before removal.
DOM manipulation belongs in ngAfterViewInit: @ViewChild references are resolved and the DOM is present. Prefer Renderer2 over direct DOM access for SSR safety.
Q5.What is the difference between an Attribute Directive and a Structural Directive?
An attribute directive changes the appearance or behavior of an existing element, while a structural directive changes the DOM layout by adding or removing elements. The key difference is that structural directives manipulate the template itself.
Attribute directive:
Modifies the host element it sits on (styles, classes, properties, event handling) without altering DOM structure.
Examples: ngClass, ngStyle, and custom directives using @HostBinding.
Structural directive:
Adds, removes, or repeats DOM elements by working with a TemplateRef and ViewContainerRef.
Prefixed with * (e.g. *ngIf, *ngFor), which Angular desugars into an <ng-template>.
Practical rule: Only one structural directive is allowed per element; use attribute directives freely alongside.
Q6.How do you create a custom pipe in Angular, and what are the tradeoffs versus doing the transformation in the component class?
You create a pipe by decorating a class with @Pipe and implementing PipeTransform. The tradeoff is reusability and template-level clarity versus the caching and testability of computing values in the component.
How to create one: Give it a name, implement transform(value, ...args), and declare it (standalone or in a module).
Advantages of a pipe: Reusable across templates, declarative in the view, and pure pipes are memoized so the transform only reruns when inputs change.
Advantages of the component class: Better for one-off logic, easier to debug, and you avoid calling a method directly in the template (which reruns every cycle without caching).
Rule of thumb: Use a pipe for reusable, presentation-only transforms; compute in the class (or a computed signal) when logic is specific or stateful.
Q7.What is the difference between ngClass and ngStyle, and how do they compare to plain class and style bindings?
ngClass and ngStyle, and how do they compare to plain class and style bindings?ngClass and ngStyle set multiple classes or inline styles dynamically from an object or expression, while plain class and style bindings target a single class or property. Use the plain bindings when you can; reach for the directives when you need many values at once.
ngClass: Adds/removes multiple CSS classes, typically via an object map like { active: isActive, disabled: isDisabled }.
ngStyle: Sets multiple inline styles from an object like { color: c, 'font-size.px': size }.
Plain bindings: [class.active]="isActive" toggles one class; [style.color]="c" sets one property. They are more direct and slightly faster.
When to choose which: Single, known target: use plain bindings. Several toggled together or a computed map: use the directive.
Q8.What are template reference variables, and how do you use them to access DOM elements or component instances?
DOM elements or component instances?Template reference variables (declared with #name) give you a named handle inside a template to a DOM element, a directive, or a component instance, which you can then read in the template or pass to a method.
Referencing a DOM element: By default #var on a plain element refers to that HTMLElement, so you can read var.value.
Referencing a component/directive:
On a component element, #var points to that component instance, exposing its public members.
Use #var="exportAsName" to grab a specific directive that sets exportAs (e.g. #f="ngForm").
Scope: Visible only within the template; to access them in the class use @ViewChild.
Q9.Explain the different types of data binding in Angular: interpolation, property binding, attribute binding, and event binding.
Data binding connects your component class and template; Angular offers one-way flows (interpolation, property, attribute binding: class to view) and event binding (view to class), which combine to form two-way binding.
Interpolation {{ }}: Embeds a component expression as text into the template; effectively sugar for binding to the textContent.
Property binding [prop]: Sets a DOM property or a component @Input, e.g. [disabled]="isBusy"; values keep their real type.
Attribute binding [attr.x]: Sets an HTML attribute when no matching DOM property exists (e.g. [attr.aria-label], [attr.colspan]).
Event binding (event): Listens for DOM events or @Output emissions; the $event payload is available in the handler.
Q10.How do @Output and EventEmitter enable child-to-parent communication in Angular?
@Output and EventEmitter enable child-to-parent communication in Angular?A child emits data upward by exposing an @Output() property that is an EventEmitter; the parent listens with event binding on the child selector, and the emitted value arrives as $event.
In the child: Declare @Output() saved = new EventEmitter<T>() and call this.saved.emit(payload).
In the parent: Bind with (saved)="onSaved($event)"; $event is the emitted value.
Under the hood:
EventEmitter extends RxJS Subject; Angular subscribes to it when wiring the output binding.
It's the standard one-way-up complement to @Input (one-way-down).
Q11.What is the purpose of ng-container, and when would you use it instead of a wrapper element?
ng-container, and when would you use it instead of a wrapper element?ng-container is a logical, invisible wrapper: it groups elements or hosts a structural directive without emitting any actual DOM node. Use it instead of a real element whenever you need grouping but adding a <div> would pollute the DOM or break CSS/layout.
Avoids extra DOM: Renders nothing itself, so it won't interfere with flexbox/grid, table structure, or styling that depends on direct children.
Hosting structural directives:
Lets you apply *ngIf and *ngFor together without stacking two on one element (which Angular forbids).
Also handy for [ngSwitch] cases or ngTemplateOutlet.
When to prefer a real element: If you actually need a node to attach classes, styles, event handlers, or ARIA attributes to.
Q12.What is the purpose of the providedIn: 'root' property in an @Injectable decorator?
providedIn: 'root' property in an @Injectable decorator?providedIn: 'root' registers a service as a single application-wide singleton on the root injector, without needing to list it in any module's providers array, and makes it tree-shakable.
Singleton scope: One instance is shared across the whole app, so all consumers see the same state.
Tree-shakable: The service registers itself; if nothing injects it, the bundler removes it from the final bundle.
No boilerplate: You don't touch any providers array; the decorator wires everything up.
Other scopes exist: You can also target a specific injector, but 'root' is the standard choice for global singletons.
Q13.Explain the AsyncPipe. Why is it considered a best practice compared to manual .subscribe() calls?
AsyncPipe. Why is it considered a best practice compared to manual .subscribe() calls?The AsyncPipe subscribes to an Observable (or Promise) directly in the template, unwraps its latest value for rendering, and automatically unsubscribes when the component is destroyed.
What it does:
Used as {{ data$ | async }}; it manages the subscription lifecycle for you.
Triggers change detection when a new value arrives (and marks OnPush components for check).
Why it beats manual .subscribe():
Automatic cleanup: unsubscribes on destroy, preventing memory leaks you'd otherwise handle in ngOnDestroy.
Less boilerplate: no manual subscription fields or unwrapping into component properties.
Encourages a reactive, declarative template and works cleanly with OnPush.
Tips: Avoid multiple async pipes on the same stream (multiple subscriptions); use *ngIf="data$ | async as data" or the new @if to share one value.
Q14.What is the difference between route parameters and query parameters, and how do you read each?
Route parameters are part of the URL path and identify a specific resource; query parameters are optional key/value pairs after ? used for filtering, sorting, or paging. Both are read from ActivatedRoute.
Route params:
Declared in the path with a colon: users/:id; usually required to identify the resource.
Read via route.paramMap (observable) or route.snapshot.paramMap.
Query params:
Appended as ?page=2&sort=name; optional and shared across routes.
Read via route.queryParamMap; set them with [queryParams] on routerLink.
Snapshot vs observable: Use the observable maps when the component can be reused for different params without reconstruction; use snapshot only when the value never changes during the component's life.
Q15.What are the roles of router-outlet and routerLink, and how do nested/child routes work?
router-outlet and routerLink, and how do nested/child routes work?A router-outlet is the placeholder where the router renders the matched component; routerLink is the declarative directive that navigates without a full page reload. Nested routes work by placing an outlet inside a routed component and defining children in the config.
router-outlet:
Marks where the active route's component is inserted; each outlet renders one level of the route tree.
Named outlets (<router-outlet name="aux">) support secondary/auxiliary routes.
routerLink:
Navigates via SPA history instead of href; accepts an array for segments: [routerLink]="['/users', id]".
Pair with routerLinkActive to toggle a CSS class on the active link.
Nested/child routes:
A parent route defines children; the parent component hosts its own router-outlet where children render.
This lets parent layout persist while inner content changes.
Q16.What are the different form control states (pristine, dirty, touched, untouched, valid, invalid) and how are they used?
pristine, dirty, touched, untouched, valid, invalid) and how are they used?These are boolean flags on every control that describe two independent dimensions: interaction (has the user touched or changed the field) and validity (does the value pass its validators). They let you show errors at the right moment instead of immediately.
Interaction states:
pristine / dirty: value unchanged vs the user has modified the value.
untouched / touched: the field has not vs has been blurred (focus left).
Validity states:
valid / invalid: all validators pass vs at least one fails.
Also pending while async validators run.
Typical usage:
Show an error only when invalid && (dirty || touched) so pristine fields aren't flagged prematurely.
Angular mirrors these as CSS classes (ng-dirty, ng-touched, ng-invalid) for styling.
They are read-only; change them with methods like markAsTouched() or markAsPristine() (useful after a reset or submit).
Q17.How do you make a typed HTTP request with HttpClient and handle error responses?
HttpClient and handle error responses?You type an HTTP request by passing the expected response shape as a generic to the HttpClient method, and you handle errors with the catchError RxJS operator, inspecting the HttpErrorResponse.
Typing the request: http.get<User>(url) returns Observable<User>; the generic is a compile-time cast, not runtime validation.
Handling errors:
Pipe catchError and receive an HttpErrorResponse with status, error, and message.
Distinguish client/network errors (status === 0) from server errors.
Re-throw with throwError or return a fallback value.
Tip: keep global handling in an interceptor and use catchError locally only when a specific recovery is needed.
Q18.What is the difference between eager loading, lazy loading, and deferred loading?
They describe when code is loaded: eager loading ships it in the initial bundle, lazy loading fetches a route's code on navigation, and deferred loading (@defer) loads a block of template inside a page based on a trigger.
Eager loading:
Modules/components bundled and loaded at app startup: fastest to become interactive but grows the initial bundle.
Use for the shell and anything needed immediately.
Lazy loading:
Route-level code split via loadComponent / loadChildren; the chunk downloads only when the user navigates there.
Shrinks the initial bundle and speeds first load.
Deferred loading:
The @defer block lazy-loads part of a template within an already-loaded page.
Triggers include on viewport, on idle, on interaction, on hover, with @placeholder and @loading states.
Granularity summary: Eager = whole app; lazy = per route; deferred = per component block inside a view.
Q19.Why would you use the NgOptimizedImage directive instead of a standard <img> tag?
NgOptimizedImage directive instead of a standard <img> tag?NgOptimizedImage (the ngSrc directive) enforces and automates image performance best practices that a raw <img> leaves to the developer, improving Core Web Vitals like LCP.
What it does automatically:
Sets loading and fetchpriority appropriately, lazy-loading below-the-fold images and prioritizing the LCP image.
Generates a srcset for responsive images and integrates with CDN image loaders.
Preconnects and can preload the priority image.
What it enforces:
Requires explicit width and height (or fill) to prevent layout shift (CLS).
Warns about oversized images and missing best practices at dev time.
Usage: Mark the hero/LCP image with priority so it isn't lazy-loaded.
Q20.How do you bootstrap an Angular application without an AppModule?
AppModule?You bootstrap with the standalone API bootstrapApplication(), passing a root standalone component and an application config where you register providers, instead of using platformBrowserDynamic().bootstrapModule(AppModule).
Entry point: main.ts calls bootstrapApplication(AppComponent, appConfig).
App-level providers:
The providers array in ApplicationConfig replaces what used to go in AppModule.
Use provider functions like provideRouter(), provideHttpClient(), and provideAnimations() instead of importing feature modules.
Q21.Describe the 'Smart vs. Dumb' (Container vs. Presentational) component pattern, and why is it beneficial for large-scale Angular apps?
It splits components into two roles: smart/container components handle data and logic (fetching, state, dispatching actions), while dumb/presentational components just render inputs and emit outputs. This separation keeps UI reusable and business logic testable.
Smart (container) components:
Inject services, subscribe to data, manage state, and coordinate child components.
Know how the app works but contain minimal markup.
Dumb (presentational) components:
Receive data via @Input() and communicate up via @Output(); no service injection.
Pure and reusable, ideal for OnPush change detection.
Why it scales:
Presentational components are easy to test (inputs in, outputs out) and reuse across features.
Logic centralizes in a few containers, so refactors and state changes touch fewer files.
Q22.Can you mix Standalone Components and NgModules in the same project, and if so, how?
NgModules in the same project, and if so, how?Yes, and interoperability is fully supported: a standalone component can be imported by an NgModule, and a standalone component can import an NgModule to use its exported declarables. This lets you migrate incrementally.
Use a standalone component inside a module: Add it to the module's imports array (not declarations, since it isn't owned by the module).
Use a module's components inside a standalone component: Add the NgModule to the standalone component's imports (e.g. CommonModule, ReactiveFormsModule).
Migration strategy: Convert leaf components first, then containers, then remove modules; there is a schematic (ng generate @angular/core:standalone) to automate much of it.
Q23.Explain the execution order of lifecycle hooks. When does ngOnChanges trigger relative to ngOnInit?
ngOnChanges trigger relative to ngOnInit?Angular calls lifecycle hooks in a fixed sequence tied to change detection. ngOnChanges runs first (before ngOnInit) and again on every subsequent input change, while ngOnInit runs exactly once after the first ngOnChanges.
ngOnChanges: Fires whenever a bound @Input changes; called before ngOnInit on the first pass. Skipped entirely if the component has no inputs.
ngOnInit: Runs once after the first ngOnChanges; inputs are now set, so it's the place for initialization logic.
ngDoCheck: Runs on every change detection cycle, after ngOnChanges/ngOnInit.
ngAfterContentInit then ngAfterContentChecked: After projected content (ng-content) is initialized/checked.
ngAfterViewInit then ngAfterViewChecked: After the component's own view and child views are initialized/checked.
ngOnDestroy: Just before the component is torn down.
Key point: ngOnChanges precedes ngOnInit once, then repeats on each input change while ngOnInit never runs again.
Q24.What is the difference between ngOnChanges and a TypeScript setter on an @Input property?
ngOnChanges and a TypeScript setter on an @Input property?Both let you react to input changes, but a setter fires only for its own property and gives immediate access to the new value, while ngOnChanges receives all changed inputs together in one SimpleChanges object with previous and current values.
Input setter:
Scoped to a single input; runs the instant that value is assigned, which is convenient for transforming or validating one property.
No built-in access to the previous value unless you store it yourself.
ngOnChanges:
Fires once per change detection pass with every changed input; ideal when logic depends on multiple inputs at once.
Gives previousValue, currentValue, and firstChange for each property.
Rule of thumb: Use a setter for per-property reactions; use ngOnChanges when you need coordination across inputs or previous values.
Q25.What is DestroyRef, and how does it enable cleanup logic outside of ngOnDestroy?
DestroyRef, and how does it enable cleanup logic outside of ngOnDestroy?DestroyRef is an injectable service (Angular 16+) that lets you register cleanup callbacks tied to a component, directive, or service's destruction, so you can run teardown logic without implementing ngOnDestroy.
Register callbacks with onDestroy: Inject it and call destroyRef.onDestroy(() => ...); the callback fires when the host is destroyed.
Works outside components: Because it's injectable, services and functions in an injection context can hook into destruction, something ngOnDestroy (class-only) can't do.
Powers takeUntilDestroyed: The RxJS operator takeUntilDestroyed() uses DestroyRef to auto-unsubscribe, eliminating manual Subscription bookkeeping.
Multiple callbacks: You can register several, keeping cleanup logic co-located with the code that created the resource.
Q26.What is the purpose of ngAfterContentChecked and ngAfterViewChecked, and how do they differ from the corresponding init hooks?
ngAfterContentChecked and ngAfterViewChecked, and how do they differ from the corresponding init hooks?ngAfterContentChecked and ngAfterViewChecked run after Angular checks a component's projected content and its view, respectively, on every change detection cycle. The Init counterparts run only once, whereas these Checked hooks repeat on each cycle.
ngAfterContentChecked: Fires after change detection checks the content projected via ng-content; runs after ngAfterContentInit and on every subsequent cycle.
ngAfterViewChecked: Fires after the component's own view and child views are checked; runs after ngAfterViewInit and repeats each cycle.
Init vs Checked: The Init hooks fire a single time for one-time setup; the Checked hooks fire repeatedly to react to ongoing checks.
Caution: Changing bound data inside these hooks can trigger ExpressionChangedAfterItHasBeenCheckedError in dev mode; keep logic lightweight and side-effect free.
Q27.Explain the new built-in control flow (@if, @for, @switch). How does it perform compared to the legacy structural directives?
@if, @for, @switch). How does it perform compared to the legacy structural directives?The built-in control flow (@if, @for, @switch) is a template syntax (stable in Angular 17) that replaces the structural directives *ngIf, *ngFor, and *ngSwitch. It's built into the compiler rather than being directives, making it faster and requiring no imports.
@if: Supports @else if and @else natively, plus binding the result to a variable with as.
@for: Requires a mandatory track expression for efficient DOM diffing, and offers a built-in @empty block plus contextual variables like $index, $first, $last.
@switch: Uses @case and @default with no separate container element.
Performance:
Compiled directly into optimized instructions instead of instantiating directive classes, reducing overhead.
@for's required track delivers far faster list re-rendering; Angular reports up to ~90% improvement in some list-heavy cases.
Smaller runtime and better tree-shaking since the control flow isn't shipped as separate directives.
Q28.Why is the track (or trackBy) function essential when rendering large lists with @for or *ngFor?
trackBy) function essential when rendering large lists with @for or *ngFor?The track function tells Angular how to identify each item across change detection so it can reuse existing DOM nodes instead of destroying and recreating them. Without it, small data changes can trigger a full re-render of the list.
Default identity is object reference: Without track, Angular compares items by reference, so replacing the array (e.g. after an HTTP refetch) makes every item look new and the whole list is rebuilt.
A stable key enables DOM reuse: Tracking by a unique, stable id lets Angular match old and new items, so it only adds, removes, or moves the DOM nodes that actually changed.
Performance and state preservation: Reusing nodes avoids expensive DOM churn and preserves component state, focus, and animations within each row.
In modern Angular @for it is mandatory: The @for block requires a track expression; *ngFor used the optional trackBy function.
Q29.What is the difference between a pure and an impure pipe, and how does purity affect when the pipe is executed?
A pure pipe (the default) runs only when its input reference changes, making it fast and cacheable; an impure pipe runs on every change detection cycle, which is flexible but potentially expensive.
Pure pipe:
Angular re-executes transform() only when the input value changes by reference (primitive value or new object/array reference).
Mutating an array in place will NOT trigger it, which is a common gotcha.
Impure pipe:
Declared with pure: false; runs on every change detection cycle regardless of whether inputs changed.
Needed for things like filtering a mutated collection or the async pipe, but can hurt performance if the transform is heavy.
Why purity matters: Purity is an optimization contract: pure pipes let Angular skip redundant work, so prefer pure and pass new references rather than mutating.
Q30.What are @HostBinding and @HostListener, and how are they used in custom attribute directives?
@HostBinding and @HostListener, and how are they used in custom attribute directives?@HostBinding binds a property, attribute, class, or style on the directive's host element, and @HostListener subscribes to events on that host. Together they let an attribute directive react to and manipulate its host element declaratively.
@HostBinding:
Binds a class property to a host property/attribute, e.g. @HostBinding('class.active') or @HostBinding('style.color').
When the property changes, Angular updates the host during change detection.
@HostListener: Registers a method as an event handler on the host, e.g. @HostListener('mouseenter'), with access to the event via $event.
Why use them: They keep host interaction declarative and avoid manual Renderer2 or addEventListener wiring, and listeners are cleaned up automatically.
Q31.What is Renderer2, and why should you use it instead of directly manipulating the DOM?
Renderer2, and why should you use it instead of directly manipulating the DOM?Renderer2 is Angular's abstraction for manipulating the DOM through a service instead of touching nativeElement directly. It decouples your code from the browser DOM so the same logic works across rendering environments and stays safe.
Platform independence:
Direct DOM access (element.nativeElement.style) assumes a browser; it breaks in server-side rendering (Angular Universal), web workers, or native renderers.
Renderer2 delegates to whatever renderer the platform provides.
Security: Going through the renderer keeps operations aligned with Angular's sanitization and avoids arbitrary DOM injection risks.
Common API methods: createElement(), setAttribute(), addClass(), setStyle(), listen(), appendChild().
Typical use: Inject it in a directive or component and combine with ElementRef to safely modify the host element.
Q32.How do you use ngTemplateOutlet to render a template with context, and what is a use case for passing context data?
ngTemplateOutlet to render a template with context, and what is a use case for passing context data?ngTemplateOutlet renders an <ng-template> you reference, and its companion ngTemplateOutletContext injects a data object the template can bind to via let- variables. This lets you reuse one template with different data.
Basic mechanics: You pass a TemplateRef to [ngTemplateOutlet] and a context object to [ngTemplateOutletContext].
Binding context: Named properties bind with let-x="key"; the special $implicit key binds to a bare let-x.
Use cases:
Customizable/reusable UI (letting a parent supply a row or item template to a list component).
Avoiding duplicated markup by rendering the same template with varying data.
Q33.What is the difference between property binding and attribute binding, and when must you use attr. prefix?
attr. prefix?Property binding targets a DOM property (a live JavaScript object member), while attribute binding targets an HTML attribute (the initial value in markup). You must use the attr. prefix when the element has no corresponding DOM property.
Properties vs attributes: Attributes initialize; properties hold current state. Setting value as a property updates live, but the value attribute only reflects the initial HTML.
When you need attr.:
ARIA attributes ([attr.aria-*]) and SVG attributes have no DOM property.
Table attributes like colspan and rowspan lack matching properties.
Setting [colspan]="2" throws because there's no such property; [attr.colspan]="2" works.
Removing an attribute: Binding an attr. to null removes the attribute entirely.
Q34.How does two-way binding with [(ngModel)] work under the hood, and how is the banana-in-a-box syntax desugared?
[(ngModel)] work under the hood, and how is the banana-in-a-box syntax desugared?Two-way binding is just a combination of a property binding and an event binding on the same target. The banana-in-a-box [(x)] syntax desugars into an input binding [x] plus an output binding (xChange).
The desugaring convention:
For any input x, Angular expects a matching output named xChange.
[(x)]="v" becomes [x]="v" (xChange)="v = $event".
Why ngModel works this way:
The NgModel directive exposes an @Input() ngModel and an @Output() ngModelChange.
It listens to the control's value changes and emits through ngModelChange, updating your bound property.
Requires FormsModule to be imported.
Custom components can support it: Expose paired @Input() and @Output() following the xChange naming to enable [(x)].
Q35.Explain the difference between @ViewChild and @ContentChild. When is the latter populated?
@ViewChild and @ContentChild. When is the latter populated?@ViewChild queries elements in the component's own template (its view), while @ContentChild queries projected content passed in via <ng-content>. They differ in what they see and when the reference becomes available.
@ViewChild:
Finds a child element, directive, or component declared in this component's template.
Resolved and available in ngAfterViewInit (or earlier if { static: true }).
@ContentChild:
Finds content projected into the component from the parent through <ng-content>.
Populated in ngAfterContentInit, because projected content is set up before the view.
Key distinction:
View = markup you own; Content = markup handed to you and projected.
Plural variants @ViewChildren / @ContentChildren return a QueryList.
Q36.What is content projection, and when would you use ng-content vs. ngTemplateOutlet?
ng-content vs. ngTemplateOutlet?Content projection lets a component render markup passed in by its parent, so the component controls the shell while the caller supplies the content. Use ng-content to project content the parent already wrote in the template; use ngTemplateOutlet when you need to stamp a template dynamically, repeatedly, or with a context.
Content projection defined:
The parent writes content between a component's tags; the component decides where it lands using ng-content.
Great for wrappers, cards, modals, and layout components that don't know their content ahead of time.
Use ng-content when:
The content is static markup the caller supplies once; it's projected as-is.
You can't render it conditionally or repeat it: it's placed exactly where the ng-content slot sits.
Use ngTemplateOutlet when:
You need to render a template multiple times, conditionally, or pass data into it via a context.
Common for customizable item templates (e.g. a grid letting the caller define a row template).
Rule of thumb: fixed slot for caller markup, use ng-content; programmatic/repeated rendering with context, use ngTemplateOutlet.
Q37.What is 'Content Projection,' and how do you use ng-content to create reusable wrapper components?
ng-content to create reusable wrapper components?Content projection is the technique of letting a parent inject markup into a child component, and ng-content is the placeholder that marks where that markup appears. It's the foundation of reusable wrapper components: the component owns styling and structure while the caller owns the inner content.
How ng-content works:
Whatever sits between the component's opening and closing tags is transplanted into the ng-content slot.
The projected content stays bound to the parent's scope, not the child's.
Building a wrapper:
Define the reusable frame (padding, border, header) once and drop an ng-content where the body goes.
Multiple slots: use the select attribute to route content into named regions.
Why it matters: avoids inheritance and duplicated layout; one shell, infinite contents.
Q38.Explain the difference between ng-content, ng-template, and ng-container. When is each appropriate?
ng-content, ng-template, and ng-container. When is each appropriate?They solve three different problems: ng-content projects caller-supplied content, ng-template defines a chunk of markup that isn't rendered until you tell it to, and ng-container is an invisible grouping element that adds no DOM node.
ng-content:
Marks a projection slot; renders content passed in from the parent.
Appropriate for reusable wrappers and layout components.
ng-template:
Declares a template that is not rendered by default; it's instantiated via structural directives (*ngIf, *ngFor) or ngTemplateOutlet.
Appropriate for deferred, conditional, or reusable rendering with a context.
ng-container:
A logical grouping element that produces no element in the DOM.
Appropriate to host a structural directive or group nodes without adding a wrapper <div>.
Quick mnemonic: project (ng-content), defer (ng-template), group invisibly (ng-container).
Q39.What is 'content projection' and how do you use ng-content with selectors?
ng-content with selectors?Content projection places parent-provided markup into a child component, and selectors on ng-content let you route different pieces of that markup into different slots. This enables multi-region components like cards with a header, body, and footer.
The select attribute:
Accepts CSS-style selectors: element (select="h1"), class (select=".title"), or attribute (select="[header]").
Matching content goes into that slot; everything else falls into a default (unselected) ng-content.
Default slot: An ng-content with no select catches all content not claimed by a selector.
Gotchas:
Each piece of content is projected only once, into the first matching slot.
Projected content is created even if the slot isn't rendered, and lifecycle/data binding stays with the parent.
Q40.What does the static flag on @ViewChild do, and when should you set it to true?
static flag on @ViewChild do, and when should you set it to true?The static flag on @ViewChild controls when the query is resolved: static: true resolves it before change detection runs (available in ngOnInit), while static: false resolves it after (available in ngAfterViewInit). Set it true only when the target always exists and isn't inside a structural directive.
static: false (the default):
Query resolved after the first change detection, so it works even for elements behind *ngIf or *ngFor.
Read the result in ngAfterViewInit.
static: true:
Query resolved before change detection, so it's ready in ngOnInit.
Only valid when the element is unconditionally present in the template.
If the target is conditional, the query returns undefined.
When to use true:
You must access the child in ngOnInit and it's guaranteed to exist (e.g. reading a static TemplateRef).
Otherwise leave it false; that's the safe default in modern Angular.
Q41.What is the difference between ViewChildren and ContentChildren, and how do QueryLists work?
ViewChildren and ContentChildren, and how do QueryLists work?@ViewChildren queries elements/components in the component's own template, while @ContentChildren queries elements projected into it via ng-content. Both return a QueryList: a live, iterable collection that updates as matching items change.
@ViewChildren (view DOM):
Matches items declared inside this component's template.
Available in ngAfterViewInit.
@ContentChildren (projected content):
Matches items the parent projected between this component's tags.
Available in ngAfterContentInit.
By default it doesn't descend into nested children unless you pass { descendants: true }.
How QueryList works:
It's live: Angular keeps it in sync when items are added/removed (e.g. via *ngFor).
Exposes length, first, last, toArray(), forEach(), and a changes observable you can subscribe to.
Singular versions @ViewChild/@ContentChild return one element instead of a QueryList.
Q42.What is the inject() function, and what are the advantages of using it over constructor-based injection?
inject() function, and what are the advantages of using it over constructor-based injection?inject() is a function that retrieves a dependency from the current injection context without listing it in a constructor. It offers the same DI as constructor injection but with more flexibility: cleaner inheritance, reusable helper functions, and use in field initializers.
What it is:
Called during construction (field initializers, constructor, or factory functions) when an injection context is active.
Fails if called outside an injection context.
Advantages over constructor injection:
No constructor boilerplate; assign directly to fields.
Better inheritance: subclasses don't have to re-declare and pass super(...) dependencies.
Enables reusable composition functions (custom inject-based helpers shared across components).
Cleaner typing: no need for @Inject() tokens in many cases, and works well with generics.
Trade-offs: Only usable within an injection context, so you can't call it in arbitrary lifecycle methods or callbacks.
Q43.What is an InjectionToken, and when would you use it instead of a class-based provider?
InjectionToken, and when would you use it instead of a class-based provider?An InjectionToken is a unique, typed key used to register and look up a provider when you don't have (or can't use) a class as the token, such as for interfaces, primitives, or configuration objects.
Why a class isn't always enough: TypeScript interfaces and types don't exist at runtime, so they can't be DI tokens. Primitives like strings/numbers and plain config objects also have no class to inject by.
Guaranteed uniqueness: Unlike a string token, an InjectionToken instance can never collide with another, avoiding name clashes.
Type safety: It's generic (InjectionToken<T>), so inject(TOKEN) returns the correct type.
Tree-shakable defaults: Pass a factory via providedIn: 'root' so the token supplies a default without a separate provider.
Q44.What is the difference between providing a service in providedIn: 'root' versus adding it to a component's providers array?
providedIn: 'root' versus adding it to a component's providers array?The difference is scope and lifetime: providedIn: 'root' creates one app-wide singleton, while adding a service to a component's providers array creates a new instance per component (and its subtree), tied to that component's lifecycle.
providedIn: 'root':
Single shared instance for the whole application; ideal for global state, caches, and stateless helpers.
Tree-shakable: if nothing injects it, it's removed from the bundle.
Lives for the app's lifetime.
Component providers array:
A fresh instance each time the component is created, shared with its child components.
Destroyed with the component, so its state is isolated per instance.
Use for scoped/per-instance state (e.g. a form-specific store or a list-item service).
Rule of thumb: Want it global and singleton? Use root. Want isolated state per component? Use component providers.
Q45.What is the difference between providers at the component level vs. the route level in a standalone app?
Both create scoped instances, but the lifetime and reach differ: a component-level provider lives and dies with that component (and its subtree), while a route-level provider lives for as long as that route (and its children) is active.
Component-level providers (providers in @Component):
A new instance is created per component instance and shared with its template/children.
Destroyed when the component is destroyed: good for state tied strictly to one component.
Route-level providers (providers on a Route):
Create an environment injector scoped to that route and all its child routes/components.
One shared instance across everything rendered under that route: useful for feature-wide state.
Torn down when you navigate away from the route.
Key contrast: Component providers give per-instance isolation; route providers give a per-feature singleton without polluting the root injector.
Q46.Explain the difference between useClass, useValue, useFactory, and useExisting provider strategies.
useClass, useValue, useFactory, and useExisting provider strategies.These are the four ways to tell Angular's DI how to produce the value for a token: instantiate a class, hand back a literal, run a factory function, or alias one token to another.
useClass: Angular instantiates the given class (resolving its own dependencies): lets you swap an implementation behind a token.
useValue: Provides a ready-made static value: config objects, constants, or mocks in tests.
useFactory: Runs a function to compute the value, useful when creation needs logic or other injected deps (declared via deps).
useExisting:
Aliases the token to another existing token: both resolve to the same single instance (no new object).
Contrast with useClass, which would create a separate instance.
Q47.What is an Angular Signal, and how does it differ from a standard RxJS Observable?
Signal, and how does it differ from a standard RxJS Observable?A Signal is a reactive container holding a value that notifies consumers when it changes; you read it by calling it like a function. Unlike an Observable, it is synchronous, always holds a current value, and tracks its dependencies automatically for fine-grained change detection.
Signals are values, not streams:
Always have a current value read synchronously via mySignal(); no subscription needed.
Consumers (computed, effect, templates) are re-evaluated automatically when the value changes.
Observables are streams over time:
Emit zero or more values, can be async, and require subscribe (with cleanup to avoid leaks).
Rich operator ecosystem for combining, debouncing, and handling async events.
Key differences:
Signals: synchronous, glitch-free, auto-tracked dependencies, no explicit unsubscribe.
Observables: better for async/event pipelines; Signals for synchronous UI state.
They interoperate via toSignal() and toObservable().
Q48.How do signal-based inputs and outputs improve upon the legacy @Input and @Output decorators?
@Input and @Output decorators?Signal-based inputs and outputs replace the decorators with functions (input() and output()) that give you reactive, type-safe, and less error-prone component APIs: inputs become readable signals you can compute from, and outputs become simpler emitters.
Inputs are reactive signals:
An input() is a read-only signal, so you can derive computed values and react in effect without ngOnChanges.
Better type safety: input.required<T>() enforces a value at compile time, avoiding undefined-until-set bugs.
Cleaner two-way binding: model() creates a writable input that supports the banana-in-a-box [(value)] syntax without a separate output.
Simpler outputs: output() returns a lightweight emitter (not a full EventEmitter/Subject), reducing RxJS overhead for plain events.
Overall benefits: No decorators/metadata, fewer lifecycle hooks, and inputs integrate directly with the signal reactivity graph.
Q49.Explain the difference between signal, computed, and effect, and when would you use each?
signal, computed, and effect, and when would you use each?They are the three core building blocks of Signals: signal() holds writable state, computed() derives read-only state from other signals, and effect() runs side effects when signals it reads change.
signal():
A writable source of truth; update it with .set() or .update().
Use for the raw state your component owns.
computed():
A read-only derived signal that recalculates lazily and is memoized (only recomputes when dependencies change).
Use for values expressed in terms of other signals, avoiding manual synchronization.
effect():
Runs a side effect whenever any signal it reads changes: logging, DOM/third-party sync, persistence.
Not for producing values; use computed for that.
Q50.What is the difference between the traditional @ViewChild decorator and the new viewChild() signal-based query?
@ViewChild decorator and the new viewChild() signal-based query?Both query child elements/components, but @ViewChild is a decorator resolved during lifecycle hooks, while viewChild() is a signal-based query that returns a reactive signal you read like any other signal.
Timing and availability:
@ViewChild results are only reliably available in ngAfterViewInit (or ngAfterContentInit for content), and you must manage timing yourself.
viewChild() returns a signal that updates automatically when the queried element becomes available, so you can consume it in computed() or effect().
Reactivity and composition: The signal query integrates with the reactive graph, so derived state stays in sync without manual hooks.
Required variant: viewChild.required() guarantees a non-undefined result, removing null checks.
Type: @ViewChild assigns a plain property; viewChild() returns a Signal, so you call it to read the value.
Q51.What is a model() signal, and how does it enable two-way binding with signals?
model() signal, and how does it enable two-way binding with signals?A model() is a special writable signal input that supports two-way binding: it acts as both an input and an output, so a parent can bind with the banana-in-a-box syntax and the child can update the value back.
What it combines:
It is a writable signal (.set()/.update()) that the child owns.
Angular automatically creates a paired output named <name>Change, satisfying the [()] contract.
How the binding flows: Parent passes a value in; when the child writes to the model, the change propagates back and the parent's bound signal updates.
Variants: model.required() makes the input mandatory; otherwise you provide a default value.
Why it matters: It replaces the old @Input() + @Output() pair for custom two-way bindings with a single reactive declaration.
Q52.Explain the difference between a Subject, a BehaviorSubject, and a ReplaySubject in the context of an Angular State Service.
Subject, a BehaviorSubject, and a ReplaySubject in the context of an Angular State Service.All three are multicasting Subjects, but they differ in what a late subscriber receives: a Subject emits nothing on subscribe, a BehaviorSubject replays the single latest value, and a ReplaySubject replays a configurable buffer of past values.
Subject:
No initial value and no memory: subscribers only get values emitted after they subscribe.
Rarely ideal for state, since late subscribers miss the current value.
BehaviorSubject:
Requires an initial value and always holds the latest value.
New subscribers immediately receive the current state: the natural choice for a state service.
Read the current snapshot synchronously via .value.
ReplaySubject:
Buffers the last N values (and optionally a time window) and replays them to new subscribers.
Useful for history/caching; ReplaySubject(1) behaves like a BehaviorSubject but without a mandatory initial value.
State-service takeaway: expose state as an Observable via .asObservable() and usually back it with a BehaviorSubject.
Q53.What are the different ways to prevent memory leaks in Angular components? Compare takeUntilDestroyed(), the async pipe, and manual unsubscribing.
takeUntilDestroyed(), the async pipe, and manual unsubscribing.Leaks happen when subscriptions outlive the component; you prevent them by tearing subscriptions down on destroy. Prefer the async pipe and takeUntilDestroyed() over manual unsubscribing, which is the most error-prone.
async pipe:
Subscribes in the template and automatically unsubscribes when the component is destroyed.
Best default when the value is only consumed in the template: no cleanup code at all.
takeUntilDestroyed():
An operator (Angular 16+) that completes the stream when the injection context / component is destroyed.
Call it in an injection context, or pass a DestroyRef to use it elsewhere.
Best when you need to run logic in subscribe(), not just display a value.
Manual unsubscribing:
Store the Subscription and call unsubscribe() in ngOnDestroy.
Verbose and easy to forget one; only justified for imperative, non-Angular resources.
Note: streams that complete on their own (e.g. HttpClient calls) don't leak, but adding cleanup is still safe practice.
Q54.When would you use an Angular Signal versus an RxJS Observable, and what are the primary trade-offs between them?
Signal versus an RxJS Observable, and what are the primary trade-offs between them?Use a Signal for synchronous, in-component reactive state that the template reads; use an Observable for asynchronous streams and complex event orchestration over time. They complement each other rather than compete.
Signals:
Synchronous: always hold a current value you read by calling mySignal().
Great for UI/component state, derived values via computed(), and fine-grained change detection.
Simpler mental model, no subscription management or operators.
Observables:
Asynchronous streams of multiple values over time (HTTP, WebSockets, events).
Rich operators for combining, debouncing, cancellation, retry.
Require subscription/cleanup and can be lazy or cold.
Trade-offs:
Signals can't natively model async timing or backpressure; Observables can.
Observables carry more complexity for simple state.
Interop bridges exist: toSignal() and toObservable().
Rule of thumb: async pipeline, reach for RxJS; synchronous state read in the template, reach for signals.
Q55.Explain the difference between switchMap, mergeMap, concatMap, and exhaustMap specifically in the context of handling HttpClient requests.
switchMap, mergeMap, concatMap, and exhaustMap specifically in the context of handling HttpClient requests.All four are higher-order mapping operators that flatten an inner HttpClient Observable, but they differ in how they handle a new source emission while a previous request is still in flight: cancel, run concurrently, queue, or ignore.
switchMap:
Cancels the previous request and switches to the newest one.
Ideal for type-ahead search / autocomplete where only the latest result matters.
mergeMap:
Runs all requests concurrently and merges results as they arrive; no cancellation.
Good for independent parallel writes, but order isn't guaranteed.
concatMap:
Queues requests and runs them one at a time in order, waiting for each to complete.
Use when order matters (e.g. sequential dependent saves).
exhaustMap:
Ignores new emissions while a request is in flight until it completes.
Perfect for preventing duplicate submits from rapid button clicks (login/save).
Quick summary: switchMap cancels, mergeMap parallelizes, concatMap serializes, exhaustMap drops.
Q56.When would you use a computed() signal versus a derived Observable using the map operator?
computed() signal versus a derived Observable using the map operator?Use computed() when the derived value stays inside Angular's synchronous signal graph and feeds the template or other signals; use a derived Observable with map when the source is an asynchronous stream you need to transform over time.
computed():
Synchronous and pull-based: it recalculates lazily only when a dependency signal changes and something reads it.
Automatically tracks its signal dependencies (no explicit subscription list), and memoizes the result.
Best for pure UI-derived state: totals, filtered lists, formatting, enabling/disabling logic.
Derived Observable with map:
Push-based and asynchronous: transforms values as they arrive from HTTP, events, timers, or WebSockets.
Needed when you also want time-based operators (debounceTime, switchMap) that signals cannot express directly.
Rule of thumb:
Synchronous state in a component: computed(). Async streams or event pipelines: RxJS with map.
You can bridge them with toSignal() / toObservable() when a source starts in one world and is consumed in the other.
Q57.In a search-as-you-type feature, why would you choose switchMap over mergeMap or concatMap?
switchMap over mergeMap or concatMap?Choose switchMap because for search-as-you-type you only care about results for the latest query: it cancels the in-flight request when a new keystroke arrives, avoiding stale results and wasted work.
switchMap: cancel-and-switch:
Unsubscribes from the previous inner Observable when a new value emits, so an outdated HTTP call is aborted.
Guarantees the emitted result matches the most recent input.
mergeMap: keep all in flight: Runs every request concurrently and emits whichever finishes first, so a slow earlier query can arrive after a newer one and overwrite it.
concatMap: queue in order: Waits for each request to complete before starting the next, so results lag badly behind fast typing.
Typical pairing: debounceTime and distinctUntilChanged before switchMap to reduce request volume.
Q58.How do you handle multiple concurrent HTTP requests where you only care about the final result of all of them (e.g., forkJoin)?
forkJoin)?Use forkJoin when you have a fixed set of Observables that each complete and you want a single combined emission of all their last values, effectively RxJS's equivalent of Promise.all.
forkJoin behavior:
Subscribes to all sources in parallel and emits once, as an array or object of their final values.
Only emits after every source completes: ideal for one-shot HTTP calls (which complete after one value).
Key caveats:
If any source errors, forkJoin errors immediately and you lose the other results: add per-source catchError to keep partial data.
If a source never completes (like a raw Subject), forkJoin never emits.
Alternatives: combineLatest: use when sources are long-lived and you want the latest of each on every change, not a single final result.
Q59.What are Route Guards, and what is the difference between canActivate and canMatch?
canActivate and canMatch?Route guards are functions the router runs to decide whether navigation to (or away from) a route is allowed. canActivate decides whether an already-matched route can be entered, while canMatch decides whether the route is even considered a match at all.
canActivate:
Runs after the route path has matched, guarding entry (typically for authentication/authorization).
If it blocks, the route is matched but you're denied, so you usually redirect to a login page.
canMatch:
Runs during route matching: if it returns false, the router skips this route and tries the next one with the same path.
Because it runs before matching, it can prevent a lazy-loaded chunk from ever being downloaded.
Enables showing different components for the same URL based on role or feature flag.
Related guards: canActivateChild, canDeactivate (unsaved-changes warnings), and the resolver-adjacent resolve.
Q60.What is the purpose of a Resolver, and what are the trade-offs of using one versus fetching data inside ngOnInit?
ngOnInit?A Resolver pre-fetches data before a route activates, so the component renders with its data already present instead of showing an empty then loading state. The trade-off is a delay in navigation while data loads, versus a faster route change with in-component loading in ngOnInit.
Purpose of a Resolver:
Runs before activation; the router waits for it to resolve, then hands the data to the component via ActivatedRoute.data.
Guarantees the component never renders without its required data, avoiding flicker and null-checks.
Resolver trade-offs:
Downside: navigation is blocked until the fetch finishes, so a slow call makes the app feel unresponsive unless you show a global loading indicator.
Error handling is more awkward: you must cancel or redirect the navigation on failure.
Fetching in ngOnInit:
Navigation is instant; the component shows a spinner/skeleton while it loads its own data.
Simpler, more component-local error handling, but you must design for the empty/loading state.
Rule of thumb: resolve small, critical data the view can't function without; load secondary or slow data inside the component.
Q61.How does Lazy Loading work in Angular, and how do you implement it for a standalone component?
Lazy loading defers downloading a route's code until the user navigates to it, so the initial bundle stays small. For a standalone component you point a route's loadComponent at a dynamic import(), and the router fetches that chunk on demand.
How it works:
The build splits code at dynamic import() boundaries into separate chunks.
On navigation, the router downloads and instantiates the chunk, reducing time-to-interactive on first load.
loadComponent for a single standalone component: Replaces the old NgModule-based loadChildren for the common single-component case.
loadChildren for a group of routes: Still used to lazy-load a set of child routes, now via a function returning a Routes array instead of a module.
Pair with a PreloadingStrategy to load chunks in the background after startup, and use canMatch to guard the chunk before it downloads.
Q62.How does the withComponentInputBinding feature in the router simplify how we access route parameters?
withComponentInputBinding feature in the router simplify how we access route parameters?withComponentInputBinding() lets the router feed route params, query params, and resolved data directly into matching component @Input() properties, so you no longer need to inject ActivatedRoute and subscribe to read them.
What it does:
Enabled once via provideRouter(routes, withComponentInputBinding()).
Matches path params, matrix params, query params, and data/resolver output to inputs by name.
Why it simplifies things:
Removes boilerplate: no ActivatedRoute injection, no paramMap subscription, no manual unsubscribe.
Inputs update reactively when the param changes, and pair naturally with signal inputs (input()).
Caveat: input names must match the param keys; combine with a resolver's data key to bind resolved data the same way.
Q63.Explain the difference between a guard and a resolver in Angular routing, and when does each execute?
A guard decides whether navigation is allowed; a resolver pre-fetches data before the route activates. Guards return boolean/UrlTree (or an observable/promise of one), while resolvers return the data the component needs.
Guards control access:
Types include CanActivate, CanActivateChild, CanDeactivate, CanMatch, each answering a yes/no about navigation.
Returning false cancels navigation; returning a UrlTree redirects.
Resolvers pre-load data: A ResolveFn runs before activation so the component receives data ready in ActivatedRoute.data, avoiding empty-view flicker.
Execution order: Angular runs CanDeactivate (leaving current), then CanMatch/CanActivate/CanActivateChild; only if all pass do resolvers execute, then the component loads.
Modern Angular favors functional guards/resolvers using inject() over the deprecated class-based interfaces.
Q64.What is the difference between CanDeactivate and CanActivate guards, and what is a common use case for CanDeactivate?
CanDeactivate and CanActivate guards, and what is a common use case for CanDeactivate?CanActivate runs before entering a route to decide if navigation into it is allowed; CanDeactivate runs before leaving a route to decide if the user may navigate away. The classic use of CanDeactivate is warning about unsaved changes.
CanActivate:
Gatekeeps entry: auth checks, feature flags, role permissions.
Returns true, false, or a UrlTree to redirect (e.g. to a login page).
CanDeactivate:
Receives the current component instance, so it can inspect state (e.g. form.dirty) to decide.
Typically implemented generically so any component exposing a canDeactivate() method can reuse the guard.
Common use case: Prompt "You have unsaved changes, leave anyway?" when a user tries to navigate away from a dirty form.
Q65.Compare reactive forms and template-driven forms. In what scenarios is one better than the other?
Template-driven forms build the model implicitly in the template with directives like ngModel, while reactive forms define the model explicitly in the component class with FormControl/FormGroup. Reactive forms scale better for complex, dynamic, or heavily tested forms; template-driven forms are quicker for simple ones.
Template-driven:
Model created by directives; logic lives in the template, so it's concise for small forms.
Asynchronous and mutable; harder to unit test and to compose dynamically.
Reactive:
Model built in code with FormBuilder; synchronous, immutable data flow that is predictable and easy to test.
Validators are functions, and valueChanges/statusChanges observables enable reactive logic.
When to choose which:
Template-driven: simple, mostly static forms (login, contact).
Reactive: dynamic controls, cross-field validation, conditional fields, or anything needing strong testing.
Q66.How do Angular's strictly typed forms improve developer experience compared to the older untyped versions?
Typed reactive forms (Angular 14+) give FormControl, FormGroup, and FormArray real generic types, so the compiler knows the shape of your form values instead of treating everything as any. This catches errors at build time and enables editor autocompletion.
Type safety on values: form.value and getRawValue() are typed, so typos in property names or wrong types fail to compile.
Typed control access: form.get('email') returns a correctly typed control, and IDE autocomplete lists valid keys.
Nullability is explicit: By default a control's value can be null (after reset()); use nonNullable: true or NonNullableFormBuilder to exclude it.
Migration note: Legacy untyped forms remain via UntypedFormGroup etc., easing gradual upgrades.
Q67.What is the difference between a synchronous and an asynchronous validator, and how do you implement an async validator?
A synchronous validator returns a validation result immediately, while an asynchronous validator returns a Promise or Observable that resolves later, which is essential for checks that need a server round-trip (like username availability).
Sync validator signature:
A function (control: AbstractControl) => ValidationErrors | null that returns errors or null synchronously.
Registered in the second argument of a control.
Async validator signature:
Returns Observable<ValidationErrors | null> or Promise; must complete (or emit and complete) for validation to settle.
Registered in the third argument, separate from sync validators.
Execution order and status:
Async validators run only after all sync validators pass, avoiding needless server calls.
While pending, the control's status is PENDING.
Best practice: debounce and use switchMap to cancel stale requests when the value changes.
Q68.What is FormBuilder, and how do FormGroup, FormControl, and FormArray relate to each other in reactive forms?
FormBuilder, and how do FormGroup, FormControl, and FormArray relate to each other in reactive forms?FormBuilder is a helper service that reduces boilerplate when constructing reactive form models. Those models are trees built from three building blocks: FormControl (a single field), FormGroup (a keyed collection of controls), and FormArray (an indexed, dynamic list of controls).
FormControl: Represents one input's value, validation state, and status. The leaf of the tree.
FormGroup:
A fixed set of named controls; its value is an object keyed by control name.
Invalid if any child is invalid.
FormArray: An ordered list of controls accessed by index; use it when the number of fields is dynamic (add/remove rows).
All three extend AbstractControl: They share value, valid, valueChanges, etc., and nest freely (a group can hold an array of groups).
FormBuilder: Provides group(), control(), and array() so you avoid repeated new calls; inject it via inject(FormBuilder).
Q69.How do you create a custom validator in reactive forms, and how does it differ from a built-in validator?
A custom validator is just a function matching the ValidatorFn signature that returns an errors object or null; it differs from built-in validators only in that you write the logic yourself, while built-ins like Validators.required are pre-supplied by Angular.
The contract:
(control: AbstractControl) => ValidationErrors | null: return null when valid, or an object like { forbidden: true } when invalid.
The error key becomes accessible via control.errors and control.hasError('forbidden').
Parameterized validators: Wrap it in a factory that returns a ValidatorFn (this is how Validators.minLength(5) works).
Cross-field validators: Attach to a FormGroup so you can compare siblings (e.g. password vs confirm).
Difference from built-ins: Same signature and registration; built-ins are just maintained by the framework. Custom ones are used identically in the validators array.
Q70.What are valueChanges and statusChanges observables on form controls, and how would you use them?
valueChanges and statusChanges observables on form controls, and how would you use them?valueChanges and statusChanges are Observable streams on any AbstractControl: the first emits the new value whenever it changes, the second emits the validation status (VALID, INVALID, PENDING, DISABLED) whenever it changes.
valueChanges:
Reactively respond to input: dependent fields, autosave, live filtering, or triggering HTTP lookups.
Commonly piped with debounceTime, distinctUntilChanged, and switchMap.
statusChanges: React to validation transitions, e.g. enable a submit button or show a spinner while an async validator is PENDING.
Cautions:
They emit only on programmatic setValue unless you pass { emitEvent: false } to suppress it.
Always unsubscribe (takeUntilDestroyed or async pipe) to avoid leaks.
Q71.What are HttpInterceptors, and what are some common use cases for them in an enterprise application?
HttpInterceptors, and what are some common use cases for them in an enterprise application?An HttpInterceptor is middleware that sits in the HttpClient pipeline: it can inspect, transform, or short-circuit every outgoing request and incoming response, centralizing cross-cutting concerns so they aren't duplicated in each service.
How it works:
Each interceptor receives the request and a next handler; it clones and modifies the request (requests are immutable), then forwards it and can operate on the returned Observable.
They run in the order registered and form a chain.
Common use cases:
Auth: attach a bearer token to every request header.
Error handling: centralized retry, logging, and mapping error responses (e.g. redirect on 401).
Loading indicators: increment/decrement a global spinner counter.
Caching, adding base URLs, or setting common headers like Content-Type.
Modern form: Functional interceptors (HttpInterceptorFn) are plain functions registered with withInterceptors([...]).
Q72.How do you configure HttpClient in a standalone application using provideHttpClient, and how do you register interceptors?
HttpClient in a standalone application using provideHttpClient, and how do you register interceptors?In a standalone app you register HttpClient by adding provideHttpClient() to the application providers (no HttpClientModule), and you wire interceptors through the withInterceptors() feature.
Base setup: Add provideHttpClient() to the providers of bootstrapApplication (or appConfig).
Functional interceptors: withInterceptors([authInterceptor, errorInterceptor]): an array of HttpInterceptorFn, run in listed order.
Bridging legacy DI-based interceptors: Use withInterceptorsFromDi() to include class-based interceptors registered with the HTTP_INTERCEPTORS token.
Other features: withFetch() to use the Fetch API, withJsonpSupport() for JSONP.
Q73.How does Angular protect against Cross-Site Scripting (XSS)? What is the role of DomSanitizer?
DomSanitizer?Angular treats all interpolated values as untrusted by default and automatically sanitizes them before inserting into the DOM, so injected markup or scripts are stripped rather than executed. DomSanitizer is the service that performs this sanitization and, only when you explicitly ask, lets you bypass it.
Automatic contextual escaping:
Interpolation {{ value }} and property bindings like [innerHTML] are sanitized according to the context (HTML, style, URL).
Dangerous content (e.g. <script> or javascript: URLs) is removed, not rendered.
No raw innerHTML by default: Angular avoids the classic XSS vector of assigning untrusted strings directly to element.innerHTML.
Role of DomSanitizer:
Provides sanitize() for enforcing sanitization in a given SecurityContext.
Provides bypassSecurityTrust* methods to mark a value as trusted when you are certain it is safe (rare, and your responsibility).
Ahead-of-Time compilation also helps: templates are compiled, so template injection is not possible at runtime.
Q74.How does Angular protect against CSRF attacks with HttpClient's XSRF token handling?
HttpClient's XSRF token handling?Angular's HttpClient defends against CSRF using the double-submit cookie pattern: it reads a token the server sets in a cookie and echoes it back in a request header, so an attacker's forged cross-site request (which cannot read the cookie) fails server-side validation.
How it works:
Server sets a cookie named XSRF-TOKEN (readable by JS on the same origin).
Angular reads that cookie and attaches its value as the X-XSRF-TOKEN header on mutating requests.
The server compares header and cookie; a cross-site attacker can't read the cookie, so it can't forge the header.
Automatic behavior:
Applied by an interceptor only to mutating verbs (POST, PUT, DELETE), not GET/HEAD.
Only for relative/same-origin URLs, so the token isn't leaked to other domains.
Configuration:
Customize names with withXsrfConfiguration({ cookieName, headerName }) (or the older HttpClientXsrfModule).
The server must actually set the cookie and validate the header: Angular only handles the client side.
Q75.Explain the @defer block and its various triggers such as on idle, on viewport, and on hover, and how does it improve initial load time?
@defer block and its various triggers such as on idle, on viewport, and on hover, and how does it improve initial load time?The @defer block (Angular 17+) lazily loads a section of template and its dependencies only when a trigger fires, so their JavaScript is split out of the initial bundle. This shrinks the initial download and speeds up first render.
How it helps initial load:
Components, directives, and pipes used only inside the block are code-split into separate chunks.
They download and render on demand, reducing initial bundle size and time-to-interactive.
Common triggers:
on idle: loads when the browser is idle (the default trigger).
on viewport: loads when the block scrolls into view.
on hover, on interaction: loads on user hover or click/keydown.
on timer(...) and on immediate: time-based or right after render.
when <condition>: loads when an expression becomes true.
Companion blocks:
@placeholder: shown before loading; @loading: shown during fetch; @error: shown on failure.
prefetch lets you fetch chunks ahead of the actual render trigger.
Q76.Explain the difference between Emulated, ShadowDom, and None view encapsulation. Which is the default, and why?
Emulated, ShadowDom, and None view encapsulation. Which is the default, and why?View encapsulation controls how a component's styles are scoped to its template. Emulated is the default: Angular scopes styles by rewriting them with generated attributes, giving component-local styles without needing native Shadow DOM.
Emulated (default):
Angular adds unique attributes (e.g. _ngcontent-xxx) to elements and rewrites CSS selectors to match them.
Styles stay local to the component but no real Shadow DOM is used, so it works everywhere and allows global styles to still cascade in.
ShadowDom:
Uses the browser's native Shadow DOM to attach a shadow root, giving true style isolation.
Global styles do not penetrate, which can be a feature or a limitation depending on your needs.
None: No scoping at all: styles are added to the document globally and leak into other components.
Why Emulated is the default: It provides component-scoped CSS with the broadest browser compatibility and predictable interaction with global stylesheets, without the isolation surprises of native Shadow DOM.
Q77.Explain the difference between the Default and OnPush change detection strategies. When should you use each?
Default and OnPush change detection strategies. When should you use each?Both decide when a component's view is re-checked. Default checks every component on each change detection run; OnPush skips a component unless something it depends on demonstrably changed, which cuts unnecessary checks and boosts performance.
Default strategy:
On any event (click, HTTP callback, timer), Angular checks the whole component tree.
Simple and safe, but can waste work in large trees.
OnPush strategy:
A component is checked only when: an @Input reference changes, an event fires from within it, an async pipe emits, or you manually call markForCheck().
Relies on immutable data / new references: mutating an object in place won't trigger it.
When to use each:
Use OnPush for performance-sensitive components and predictable, immutable data flows (also pairs well with signals).
Use Default for simple apps or components where the cost of change detection is negligible.
Q78.How does ChangeDetectionStrategy.OnPush work, and what specific triggers cause an OnPush component to re-render?
ChangeDetectionStrategy.OnPush work, and what specific triggers cause an OnPush component to re-render?With ChangeDetectionStrategy.OnPush, Angular skips checking a component unless one of a few specific signals says its view may have changed, drastically cutting the number of checked components per cycle.
Default vs OnPush: Default checks every component on every change detection tick; OnPush marks a component as "skipped" until it is explicitly dirtied.
Triggers that mark an OnPush component for check:
An @Input() reference changes (compared by identity, so mutating an object in place won't trigger it).
A DOM event fires from within the component's own template (e.g. a (click) handler).
An async pipe (| async) receives a new emission, since it calls markForCheck() internally.
You call markForCheck() manually via ChangeDetectorRef.
A signal read in the template changes value (in modern Angular).
Why it's faster: When marked dirty, Angular checks the component and its ancestors up to the root, but prunes unmarked OnPush subtrees.
Practical consequence: OnPush pairs naturally with immutable data and observables; mutating inputs in place is the classic bug that leaves the view stale.
Q79.What is the role of NgZone and when would you explicitly use runOutsideAngular?
NgZone and when would you explicitly use runOutsideAngular?NgZone is Angular's wrapper around Zone.js that knows when async work starts and finishes, triggering change detection when the zone becomes stable; runOutsideAngular() lets you run code that should NOT trigger change detection.
What NgZone does: It tracks async tasks (timers, events, XHR) inside Angular's zone and fires onMicrotaskEmpty to run a change detection tick when work settles.
When to use runOutsideAngular():
High-frequency events that don't affect the UI: mousemove, scroll, requestAnimationFrame loops.
Frequent timers or third-party libraries (charts, animation engines) that would otherwise storm change detection.
Re-entering the zone: When outside work finally needs to update the UI, wrap it in ngZone.run() to trigger a check again.
Q80.What are hydration mismatches, and what causes them?
A hydration mismatch happens when the DOM Angular renders on the client differs from the DOM the server produced, so Angular can't safely reuse the markup. When detected, Angular logs an error and destroys and re-renders the affected subtree, losing the performance benefit of hydration.
Direct DOM manipulation: Manipulating the DOM outside Angular (native APIs, jQuery, third-party libs) changes structure Angular expects to match.
Non-deterministic rendering: Using Date.now(), Math.random(), or locale/timezone differences so server and client render different values.
Invalid HTML nesting: Browsers auto-correct invalid markup (e.g. a <div> inside a <p>), so the client DOM no longer matches the server string.
Browser-only APIs during render: Reading window or localStorage only on the client produces content the server never rendered.
Fixes: Guard browser-only code with isPlatformBrowser, write valid HTML, and use ngSkipHydration as a last resort on components that must be re-rendered.
Q81.What is the role of TransferState in an SSR application?
TransferState in an SSR application?TransferState is a key-value store Angular serializes into the server-rendered HTML so the client can read it during bootstrap, avoiding a second identical HTTP request for data already fetched on the server.
The problem it solves: Without it, the server fetches data to render, then the client re-fetches the same data on hydration: duplicate work and a flicker.
How it works:
Server stores data in TransferState; Angular embeds it as JSON in the HTML.
Client reads it back on startup instead of calling the API again.
Usage in practice:
Adding provideClientHydration(withHttpTransferCacheOptions()) (or the default) automatically caches HttpClient GET requests via TransferState.
You can also use the TransferState API directly with makeStateKey() for custom values.
Q82.How do isPlatformBrowser and isPlatformServer help in writing SSR-compatible code?
isPlatformBrowser and isPlatformServer help in writing SSR-compatible code?isPlatformBrowser and isPlatformServer are helpers that check which platform the code is executing on, letting you guard browser-only or server-only logic so the same codebase runs safely under SSR where APIs like window don't exist.
Why they are needed: SSR runs in Node, where window, document, and localStorage are undefined; touching them crashes the server render.
How to use them:
Inject the PLATFORM_ID token and pass it to the check.
Run browser-only code (DOM access, analytics, IntersectionObserver) only when isPlatformBrowser is true.
Caution: Rendering different content per platform can trigger hydration mismatches: prefer deferring browser-only side effects to ngAfterViewInit rather than changing rendered output.
Q83.Explain the difference between Ahead-of-Time (AOT) and Just-in-Time (JIT) compilation, and why is AOT mandatory for production?
AOT compiles Angular templates and components to JavaScript at build time, while JIT compiles them in the browser at runtime. AOT is mandatory for production because it is faster, safer, and smaller.
JIT (Just-in-Time):
Ships the Angular compiler to the browser and compiles templates on load.
Slower startup and larger bundle (compiler included); template errors surface only at runtime.
Historically used for development/debugging convenience.
AOT (Ahead-of-Time):
Compiles at build time so the browser downloads ready-to-run code.
Catches template/binding errors during the build, not in the user's browser.
Enables tree-shaking of the compiler and unused code.
Why AOT is mandatory for production:
Faster rendering and startup (no in-browser compile step).
Smaller payload (compiler excluded).
Security: no runtime evaluation of templates reduces injection risk.
Since Angular 9 with Ivy, AOT is the default for both dev and prod builds.
Q84.What are Angular schematics, and how does the Angular CLI use them with ng generate and ng add?
ng generate and ng add?Schematics are code generators/transformers: template-based instructions that scaffold or modify files in a project. The Angular CLI runs them to create files (ng generate) and to install and configure libraries (ng add).
What they are:
Packaged recipes that describe file operations on a virtual file system (the Tree), applied atomically.
Because they operate on a tree, changes can be previewed with --dry-run before writing.
ng generate:
Runs a schematic to scaffold code (ng generate component user) using Angular's built-in collection.
Ensures consistent, best-practice boilerplate and wiring.
ng add: Installs a package and runs its install schematic to configure it (ng add @angular/material sets up themes, modules, polyfills).
ng update: Uses migration schematics to automatically refactor code across version upgrades.
Q85.What is the purpose of TestBed, and when would you choose to use it over a plain unit test?
TestBed, and when would you choose to use it over a plain unit test?TestBed is Angular's testing utility that creates a real (but isolated) Angular environment: it configures a testing module, resolves dependency injection, and can render components. You use it when a test needs Angular's DI, templates, or change detection; you skip it for pure logic that has no Angular dependencies.
What TestBed provides:
A configurable module via TestBed.configureTestingModule() to declare components and provide/mocks services.
A working DI container so you can TestBed.inject() real or mocked dependencies.
Component fixtures via createComponent() for template rendering and detectChanges().
When to use it:
Testing a component with its template, bindings, or inputs/outputs.
Testing DI wiring, providers, pipes, or directives.
When plain unit tests are better:
Pure functions, pipes, or services with no Angular DI: instantiate directly for speed.
TestBed has setup overhead, so avoid it when it adds nothing.
Q86.How do you mock an Angular Service that uses HttpClient during a unit test?
HttpClient during a unit test?Use Angular's HttpClientTestingModule with HttpTestingController to intercept and mock HTTP calls, so the service runs its real logic while no real network request is made and you assert on the requests it issues.
Set up the testing module:
Import HttpClientTestingModule so HttpClient is backed by a mock backend.
Inject HttpTestingController to control requests.
Flow of a test:
Call the service method that triggers the HTTP call.
Use httpMock.expectOne(url) to capture the request and assert its method/body.
Respond with req.flush(mockData) to simulate the server.
Call httpMock.verify() in afterEach to ensure no unexpected requests.
Alternative: For a component that depends on the service, provide a spy/stub of the service instead (jasmine.createSpyObj) to isolate the component from HTTP entirely.
Q87.How do you use a spy in Angular tests, and what is the difference between spyOn and a mock service?
spyOn and a mock service?A spy is a test double that wraps or replaces a function so you can assert how it was called and control what it returns; spyOn patches an existing method on a real object, while a mock service is a whole fake object you supply in place of the real dependency.
What a spy does:
Records calls (arguments, count) so you can assert with matchers like toHaveBeenCalledWith().
Can stub behavior: .and.returnValue(), .and.callThrough(), or .and.callFake().
spyOn(obj, 'method'):
Takes an existing object and temporarily overrides one method, keeping the rest of the real object intact.
Best when you have a real (or injected) instance and only want to observe/override a single method.
Jasmine also offers jasmine.createSpyObj('name', ['a','b']) to build a standalone object of spies.
A mock service:
An entire replacement object (often via createSpyObj or a small fake class) provided through { provide: RealService, useValue/useClass: mock }.
Isolates the component under test from the real dependency's logic, HTTP, or state.
Key difference: spyOn instruments a method on something that already exists; a mock service replaces the whole dependency in the DI container.
Q88.Why do you need to call fixture.detectChanges() in a component test, and what happens if you don't?
fixture.detectChanges() in a component test, and what happens if you don't?You call fixture.detectChanges() to trigger Angular's change detection so bindings, lifecycle hooks, and the rendered DOM reflect the component's current state; in tests Angular does not run change detection automatically, so without it the DOM stays empty or stale.
What it triggers:
Runs the first ngOnInit (and other lifecycle hooks) on the initial call.
Evaluates template bindings, interpolation, and directives, updating the rendered DOM.
Why it's manual in tests: The test environment (with default ComponentFixtureAutoDetect off) leaves change detection to you, giving precise control over when the view syncs with the model.
If you don't call it:
ngOnInit never runs, so setup-in-init logic is skipped.
The DOM is not rendered/updated, so nativeElement queries find nothing and assertions on text/attributes fail.
Call it again after changes:
After mutating a component property or emitting new data, call detectChanges() again to re-render before asserting.
For async work, combine with fixture.whenStable() or tick() inside fakeAsync.
Q89.If you have a library of standalone components, how do you manage shared dependencies without a SharedModule?
SharedModule?Instead of one big SharedModule, each standalone component imports exactly the dependencies it needs, and you optionally group commonly-combined imports into small arrays or export barrels. This keeps dependencies explicit and tree-shakable.
Import per-component: Each component lists its own directives/pipes in imports; unused ones are never pulled in.
Reusable import arrays: Export a plain const array of frequently paired imports and spread it into components that need them.
Services via DI, not modules: Use providedIn: 'root' or provider functions (provide...()) rather than a shared module's providers.
Why avoid SharedModule: A catch-all module forces every consumer to import everything, hurting clarity and tree-shaking.
Q90.What is the 'component-first' mental model, and how does it impact tree-shaking in Angular?
The component-first mental model treats the component (with its explicit imports) as the unit of composition and dependency, rather than the module. Because dependencies are declared where they're used, the compiler and bundler can see exactly what's reachable and drop the rest, improving tree-shaking.
Dependencies are local and explicit: A standalone component states precisely what it uses, so nothing is dragged in transitively via a shared module.
Better tree-shaking: Unused components/pipes/directives aren't referenced, so the bundler eliminates them; large NgModule graphs often kept dead code alive.
Finer lazy loading: Routes can lazy-load a single component with loadComponent, so only the code for the visited route ships.
Q91.What is the purpose of ngDoCheck, and how does it relate to the change detection cycle?
ngDoCheck, and how does it relate to the change detection cycle?ngDoCheck is a custom change-detection hook: it runs on every change detection cycle and lets you detect and act on changes Angular can't catch by default, such as mutations inside objects or arrays.
Runs on every cycle: Called after ngOnChanges and ngOnInit, on each detection pass, so it can fire very frequently.
Fills the gap of reference-based detection: ngOnChanges only fires when an input reference changes; mutating an object/array in place doesn't trigger it. ngDoCheck lets you compare values manually (e.g. with KeyValueDiffers or IterableDiffers).
Performance sensitive: Because it runs constantly, keep the logic cheap; expensive work here can degrade the whole app.
Common with OnPush: Useful when a component uses ChangeDetectionStrategy.OnPush and needs to react to internal mutations it would otherwise miss.
Q92.What are afterRender and afterNextRender hooks, and how do they differ from the traditional lifecycle hooks?
afterRender and afterNextRender hooks, and how do they differ from the traditional lifecycle hooks?afterRender and afterNextRender are newer render hooks (Angular 16+) that run after the browser DOM has been rendered, and only in the browser (never during SSR). Unlike the class-based lifecycle hooks, they are registered as functions in an injection context and are designed for direct DOM reads/writes.
afterRender: Runs after every change detection cycle that produces a render; use for logic that must repeat on each render.
afterNextRender: Runs once after the next render only; ideal for one-time DOM initialization (e.g. bootstrapping a third-party library).
Differences from traditional hooks:
They are registered via function calls (usually in the constructor), not implemented as interface methods like ngAfterViewInit.
They are SSR-safe by never running on the server, avoiding DOM-access errors.
They support phases (earlyRead, read, write, mixedReadWrite) to batch DOM operations and avoid layout thrashing.
Q93.How does Angular's compiler differentiate between a Component, a Directive, and a Pipe?
Component, a Directive, and a Pipe?Angular distinguishes them by the decorator you apply and its metadata: @Component, @Directive, and @Pipe each tell the compiler what kind of class it is compiling and how it can be used in a template.
Component: A directive with a template: @Component requires a template or templateUrl and creates a view. It is technically a specialized directive.
Directive: @Directive has a selector but no template: it attaches behavior to an existing host element.
Pipe: @Pipe has a name and a transform() method; it is invoked with the | operator in templates, not via a selector.
How the compiler uses this: It reads the decorator metadata to register selectors and pipe names, then matches them against template syntax during compilation to wire up bindings and instantiation.
Q94.How do you build a custom structural directive, and what role does the asterisk syntax and TemplateRef play?
TemplateRef play?You build a structural directive with @Directive by injecting TemplateRef (the content to render) and ViewContainerRef (where to render it), then create or clear the view based on your logic. The asterisk is syntactic sugar that hands you that template.
The asterisk desugars the template: *myDir="expr" is shorthand: Angular wraps the host element in an <ng-template> and passes it as TemplateRef.
TemplateRef: A handle to the embedded template (the DOM blueprint) that is NOT rendered until you ask for it.
ViewContainerRef: The insertion point; call createEmbeddedView(templateRef) to add it and clear() to remove it.
Driving it with an input: A setter named after the selector receives the expression value and decides whether to render.
Q95.What are resolution modifiers (@Optional, @Self, @SkipSelf, @Host), and when would you use them?
@Optional, @Self, @SkipSelf, @Host), and when would you use them?Resolution modifiers are decorators that change how Angular's DI walks the injector tree when resolving a dependency: they control where the search starts, where it stops, and whether a missing token is fatal.
@Optional(): Returns null instead of throwing if the token isn't found. Use for optional collaborators or configuration that may not be provided.
@Self(): Restricts the lookup to the element's own injector only; no walking up. Use when a directive must have its dependency declared on the very same element.
@SkipSelf(): Skips the current injector and starts at the parent. Use to reach an ancestor's instance instead of the local one (e.g. a recursive tree component finding its parent's service).
@Host(): Stops the search at the host component's injector boundary. Common for directives that should only resolve within their host component's view.
They compose: e.g. @Optional() @SkipSelf() is the classic guard against re-importing a singleton module twice.
Q96.How does Angular's hierarchical dependency injection work? Explain the difference between the root injector and element injectors.
Angular DI is hierarchical: injectors form a tree that mirrors the application structure, and a request bubbles up from the requesting element until a provider is found (or it fails). There are two parallel trees: the environment (module) hierarchy topped by the root injector, and the element injector hierarchy following the component/DOM structure.
Root injector:
The application-wide injector; services with providedIn: 'root' live here as singletons shared by the whole app.
Sits near the top, above the NullInjector that throws when nothing is found.
Element injectors:
Created for each component/directive that declares providers; they follow the component tree, so a child can see providers from its ancestors.
A provider here yields an instance scoped to that component and its subtree (not a global singleton).
Resolution order: Angular searches the element injector chain first, then falls through to the environment/module injectors up to root, then the NullInjector.
Practical effect: A child re-providing a service shadows the ancestor's instance for itself and its descendants, enabling scoped state.
Q97.What is the difference between providers and viewProviders in a component decorator?
providers and viewProviders in a component decorator?Q98.Explain the concept of 'Injection Context' and why you can't call the inject() function inside a standard click handler or an asynchronous setTimeout.
inject() function inside a standard click handler or an asynchronous setTimeout.Q99.What are 'Multi-providers' in Angular, and what is a common real-world use case for them?
Q100.What are the different providedIn scopes ('root', 'platform', 'any'), and how do they differ?
providedIn scopes ('root', 'platform', 'any'), and how do they differ?Q101.How do you provide a service in the providers array of a lazy-loaded route, and what injector scope does it create?
providers array of a lazy-loaded route, and what injector scope does it create?Q102.What is the purpose of the effect() function in Signals, and why is it generally discouraged for state synchronization?
effect() function in Signals, and why is it generally discouraged for state synchronization?Q103.What is fine-grained reactivity, and how does it change how Angular updates the DOM?
DOM?Q104.What is the glitch effect in reactivity, and how do Signals prevent it?
Signals prevent it?Q105.What is a linkedSignal, and what specific problem does it solve that a standard computed signal cannot?
linkedSignal, and what specific problem does it solve that a standard computed signal cannot?Q106.Why are Signals considered zoneless-friendly?
Signals considered zoneless-friendly?Q107.What are toSignal() and toObservable(), and how do they bridge RxJS and Signals in Angular?
toSignal() and toObservable(), and how do they bridge RxJS and Signals in Angular?Q108.What is the untracked() function in Signals, and when would you need it inside a computed or effect?
untracked() function in Signals, and when would you need it inside a computed or effect?Q109.How can you provide a custom equality function to a signal, and why would you want to?
Q110.What is the resource() / rxResource() API in Angular, and what problem does it solve for async data loading?
resource() / rxResource() API in Angular, and what problem does it solve for async data loading?Q111.How do Signals improve Angular's change detection performance compared to the traditional Zone.js-based approach?
Signals improve Angular's change detection performance compared to the traditional Zone.js-based approach?Q112.How do functional router guards differ from class-based guards, and why is the framework moving toward them?
Q113.What are preloading strategies in Angular routing, and how does PreloadAllModules differ from a custom strategy?
PreloadAllModules differ from a custom strategy?Q114.What is the ControlValueAccessor interface, and why would you need it to build a custom form control?
ControlValueAccessor interface, and why would you need it to build a custom form control?Q115.What are the different security contexts Angular sanitizes (HTML, style, URL, resource URL), and how does contextual escaping work?
Q116.What is the difference between bypassSecurityTrustHtml and other bypassSecurityTrust methods, and what are the risks?
bypassSecurityTrustHtml and other bypassSecurityTrust methods, and what are the risks?Q117.What causes the ExpressionChangedAfterItHasBeenCheckedError and how do you conceptually fix it?
ExpressionChangedAfterItHasBeenCheckedError and how do you conceptually fix it?Q118.What is the difference between markForCheck() and detectChanges() in the ChangeDetectorRef?
markForCheck() and detectChanges() in the ChangeDetectorRef?Q119.How does signal-based change detection differ from the traditional 'dirty checking' of the component tree?
Q120.How do detach() and reattach() on ChangeDetectorRef work, and when would you manually detach a component from change detection?
detach() and reattach() on ChangeDetectorRef work, and when would you manually detach a component from change detection?Q121.How does Zone.js patch asynchronous browser APIs to trigger change detection?
Zone.js patch asynchronous browser APIs to trigger change detection?Q122.What is hydration in the context of Angular SSR, and how does event replay work in modern Angular versions?
Q123.What is non-destructive hydration, and how does it differ from traditional SSR?
Q124.What is Angular Universal (SSR), and how does 'Hydration' work in the latest versions of Angular?
Q125.When would you use Server-Side Rendering (SSR) versus Static Site Generation (SSG) in an Angular application?
Q126.What is 'hydration' in Angular SSR, and how does 'incremental hydration' differ from full hydration?
Q127.What is zoneless Angular, and what are the primary benefits of running an application without Zone.js?
Zone.js?Q128.What does it mean for an Angular application to be 'Zoneless', and how does the framework know when to update the UI without Zone.js?
Zone.js?Q129.What is the Ivy renderer and incremental DOM, and how does it enable better tree-shaking?
Q130.Explain the difference between fakeAsync/tick and async/await when testing asynchronous code in Angular.
fakeAsync/tick and async/await when testing asynchronous code in Angular.