Introduction
Welcome to Chapter 2 of your comprehensive Angular interview preparation! This chapter delves into the foundational building blocks of any Angular application: Components, Directives, and Pipes. Mastering these concepts is crucial for building robust, maintainable, and efficient user interfaces. Interviewers frequently assess a candidate’s understanding of these core elements, often moving beyond basic definitions to advanced use cases, performance considerations, and custom implementations.
Whether you’re an entry-level developer aiming to grasp the fundamentals, a mid-level professional looking to solidify your knowledge and optimize existing applications, or a senior engineer preparing to discuss architectural patterns and complex custom solutions, this chapter provides targeted questions and in-depth answers. We’ll cover everything from lifecycle hooks and communication strategies to custom directive and pipe creation, ensuring your knowledge is current with Angular versions 13 through 21 and modern best practices as of December 2025.
By the end of this chapter, you will be well-equipped to articulate your understanding, demonstrate practical application, and discuss advanced scenarios related to Components, Directives, and Pipes, making you a confident candidate in your next Angular interview.
Core Interview Questions
1. What is an Angular Component? Describe its core elements.
- Q: What is an Angular Component? Describe its core elements and how it interacts with the template.
- A: An Angular Component is the most fundamental building block of an Angular application, responsible for controlling a specific part of the screen called a view. It combines an HTML template for the UI, a TypeScript class for the logic, and CSS styles for presentation.
- Core Elements:
- Template: An HTML file (or inline HTML) that defines the component’s view. It uses Angular template syntax for data binding, directives, and event handling.
- Class (Controller): A TypeScript class decorated with
@Component()that contains the component’s logic, properties, and methods. It interacts with the template through data binding. - Metadata: Provided by the
@Component()decorator, it configures the component. Key properties include:selector: A CSS selector that tells Angular to create and insert an instance of this component wherever it finds the corresponding tag in the HTML.templateUrl(ortemplate): Path to the component’s HTML template file or inline HTML.styleUrls(orstyles): Paths to the component’s CSS style files or inline CSS.standalone(Angular v14+): A boolean flag to indicate if the component is standalone, meaning it doesn’t need to be declared in anNgModule.
- Styles: CSS definitions scoped specifically to the component’s template, preventing style conflicts with other parts of the application.
- Core Elements:
- Key Points:
- Components are essentially specialized directives.
- They encapsulate UI and logic.
- The
selectorproperty is crucial for using the component in other templates. - Angular’s component-based architecture promotes reusability and modularity.
- Standalone components (Angular v14+) simplify module organization.
- Common Mistakes:
- Confusing components with directives (components are directives with a template).
- Forgetting to declare a component in an
NgModule(for non-standalone components) or import necessary dependencies (for standalone components). - Overloading components with too much logic, violating the Single Responsibility Principle.
- Follow-up: How do standalone components change the way we structure Angular applications?
2. Explain the Angular Component Lifecycle Hooks.
- Q: Explain the Angular Component Lifecycle Hooks. List the most common ones and describe their typical use cases.
- A: Angular components have a lifecycle managed by Angular itself. As Angular creates, updates, and destroys components, it invokes specific lifecycle hook methods, allowing developers to tap into these moments to perform actions.
- Common Lifecycle Hooks (in order of execution):
ngOnChanges(): Called beforengOnInit(and whenever one or more data-bound input properties change). Use it to react to changes in@Input()properties.ngOnInit(): Called once after Angular initializes the component’s data-bound input properties and sets up the component. Ideal for initialization logic that doesn’t rely on constructor arguments, such as fetching initial data.ngDoCheck(): Called immediately afterngOnChangesandngOnInit(and every subsequent change detection run). Use it for custom change detection logic when Angular’s default mechanism is insufficient. (Use with caution due to performance implications).ngAfterContentInit(): Called once after Angular projects external content into the component’s view. Use it to perform initialization logic on projected content.ngAfterContentChecked(): Called afterngAfterContentInitand every subsequentngDoCheck. Use it to respond to changes in the projected content.ngAfterViewInit(): Called once after Angular initializes the component’s view and child views. Use it to interact with child components or template elements via@ViewChild()or@ViewChildren().ngAfterViewChecked(): Called afterngAfterViewInitand every subsequentngDoCheck. Use it to respond to changes in the component’s view and child views.ngOnDestroy(): Called just before Angular destroys the component. Use it to clean up, such as unsubscribing from Observables, detaching event handlers, or clearing intervals to prevent memory leaks.
- Common Lifecycle Hooks (in order of execution):
- Key Points:
- Each hook corresponds to an interface (e.g.,
OnInit,OnChanges). ngOnInitis the most commonly used for component initialization.ngOnDestroyis vital for preventing memory leaks.- The “Checked” hooks (
ngAfterContentChecked,ngAfterViewChecked) are called frequently during change detection.
- Each hook corresponds to an interface (e.g.,
- Common Mistakes:
- Performing heavy operations in
ngDoCheck()without careful optimization. - Not unsubscribing from Observables in
ngOnDestroy(). - Trying to access
@ViewChildelements beforengAfterViewInit().
- Performing heavy operations in
- Follow-up: When would
ngDoCheck()be necessary, and what are its potential pitfalls?
3. Differentiate between @Input() and @Output() decorators.
Q: Differentiate between
@Input()and@Output()decorators in Angular component communication. Provide a scenario where both are used.A:
@Input()and@Output()are decorators used for communication between parent and child components.@Input():- Purpose: Allows a child component to receive data from its parent component.
- Mechanism: Marks a property in the child component as an input property. The parent component can then bind to this property using property binding
[]in its template. - Direction: Data flows down from parent to child.
- Example: A
UserDetailComponentreceiving auserIdfrom its parent.
@Output():- Purpose: Allows a child component to send data or events up to its parent component.
- Mechanism: Marks a property in the child component as an output property. This property must be an instance of
EventEmitter<T>, which emits custom events. The parent component listens to these events using event binding()in its template. - Direction: Events/data flow up from child to parent.
- Example: A
SaveButtonComponentemitting asavedevent to its parent when clicked.
Scenario: A
ProductListComponent(parent) displays a list of products. Each product is rendered by aProductCardComponent(child).ProductListComponentpasses product data toProductCardComponentusing@Input().// product-list.component.html <app-product-card *ngFor="let product of products" [product]="product" (addToCart)="onAddToCart(product)"></app-product-card>ProductCardComponentemits anaddToCartevent when a button is clicked, sending the product back toProductListComponentusing@Output().// product-card.component.ts import { Component, Input, Output, EventEmitter } from '@angular/core'; @Component({ selector: 'app-product-card', template: ` <div> <h3>{{ product.name }}</h3> <p>{{ product.price | currency }}</p> <button (click)="addToCart.emit(product)">Add to Cart</button> </div> `, standalone: true // Example for Angular v14+ }) export class ProductCardComponent { @Input() product: any; @Output() addToCart = new EventEmitter<any>(); }
Key Points:
@Input()uses property binding[].@Output()uses event binding()andEventEmitter.- Essential for creating reusable and modular components.
Common Mistakes:
- Trying to send data from parent to child using
@Output(). - Trying to send data from child to parent directly without an
EventEmitter. - Not calling
.emit()on theEventEmitterto trigger the event.
- Trying to send data from parent to child using
Follow-up: What are alternative methods for component communication, especially for unrelated components or global state?
4. How do you optimize change detection in Angular components?
- Q: How do you optimize change detection in Angular components, especially in large applications? Discuss
OnPushstrategy and potentially signals (Angular v17+). - A: Optimizing change detection is crucial for application performance. Angular’s default change detection strategy (
Default) checks all components from top to bottom on every browser event.ChangeDetectionStrategy.OnPush: This is the primary optimization technique. When a component usesOnPush, Angular only checks it (and its subtree) if:- One of its
@Input()properties has changed (specifically, if the reference to the input object/array has changed, not just a mutation within it). - An event originated from the component or one of its children (e.g., button click).
asyncpipe is used in the template and emits a new value.ChangeDetectorRef.detectChanges()orChangeDetectorRef.markForCheck()is explicitly called.
- Benefit: Significantly reduces the number of checks, improving performance, especially in large component trees.
- One of its
- Immutability: To effectively use
OnPush, data passed via@Input()should be immutable. Instead of mutating objects/arrays, create new instances when data changes. This ensures Angular detects the reference change. - Async Pipe (
| async): Using theasyncpipe with Observables or Promises automatically marks the component for check when a new value is emitted, without needing manualdetectChanges(). It also handles subscription/unsubscription, preventing memory leaks. - Signals (Angular v17+): Signals offer a more granular and efficient way to manage reactivity and change detection. When a component uses
signals, Angular’s change detection can be optimized further, only re-rendering parts of the template that depend on changed signals, potentially moving away from zone.js-based change detection for those specific reactive elements. While still evolving, signals are a significant step towards more fine-grained reactivity and can lead to highly optimized applications.
- Key Points:
OnPushis the go-to strategy for performance.- Immutability is key for
OnPushto work effectively. asyncpipe is a best practice for handling asynchronous data.- Signals (v17+) represent a future direction for even more precise change detection.
- Common Mistakes:
- Using
OnPushbut mutating input objects/arrays, leading to components not updating. - Calling
ChangeDetectorRef.detectChanges()indiscriminately, negatingOnPushbenefits. - Not understanding when
OnPushactually triggers a check.
- Using
- Follow-up: How does
ChangeDetectorRef.markForCheck()differ fromChangeDetectorRef.detectChanges()?
5. When would you use ng-content and ng-template?
- Q: Explain the purpose of
ng-contentandng-templatein Angular. Provide use cases for each. - A: Both
ng-contentandng-templateare powerful tools for creating flexible and reusable components, but they serve different purposes.ng-content(Content Projection):- Purpose: Used to project content (HTML, other components) from a parent component into a specific slot within a child component’s template. It allows for component composition.
- Mechanism: The
<ng-content></ng-content>tag acts as a placeholder in the child component’s template. When the child component is used, any content placed between its opening and closing tags in the parent’s template will be rendered whereng-contentis located. - Use Cases:
- Layout Components: Creating a generic
CardComponentthat accepts varying content for its header, body, and footer. - Wrapper Components: A
ModalComponentthat displays custom content inside its modal body. - Library Components: Providing flexible slots for users to inject their own UI.
- Layout Components: Creating a generic
- Selector (
selectattribute): You can useselect="CSS_SELECTOR"withng-contentto project specific pieces of content based on CSS selectors (e.g.,select="[header]",select="p"). This is called multi-slot content projection.
ng-template:- Purpose: A template element that Angular uses to render structural directives (like
*ngIf,*ngFor) or for custom template rendering logic. It is not rendered directly into the DOM unless explicitly told to by a directive. - Mechanism: It defines a block of HTML that can be instantiated and rendered dynamically. It is often used implicitly by structural directives, which transform
*ngIf="condition"into<ng-template [ngIf]="condition">...</ng-template>. - Use Cases:
- Custom Structural Directives: When creating your own
*myStructuralDirective, you’ll work withng-templateto define the content to be rendered. - Conditional Rendering with
ngIf/else:<div *ngIf="loggedIn; else guestBlock"> Welcome, User! </div> <ng-template #guestBlock> Please log in. </ng-template> ngForwithtrackBy: While not directlyng-template,ngForinternally usesng-templateto stamp out items.- Dynamic Component Loading: Using
ViewContainerRef.createEmbeddedView()to programmatically instantiate a template.
- Custom Structural Directives: When creating your own
- Purpose: A template element that Angular uses to render structural directives (like
- Key Points:
ng-contentis for content projection (passing content into a component).ng-templateis for template definition (a blueprint for rendering content conditionally or repeatedly).ng-templateitself is never rendered; it’s a structural instruction.
- Common Mistakes:
- Confusing the roles of
ng-contentandng-template. - Trying to directly display
ng-templatecontent without a structural directive or programmatic instantiation. - Forgetting that
ng-contentis typically used in the child component’s template to receive content from the parent.
- Confusing the roles of
- Follow-up: How does content projection (
ng-content) relate to@ContentChild()and@ContentChildren()?
6. What are Directives in Angular? Differentiate between Component, Structural, and Attribute Directives.
- Q: What are Directives in Angular? Differentiate between Component, Structural, and Attribute Directives, providing an example for each type.
- A: Directives are classes that add extra behavior to elements in Angular applications. They allow you to manipulate the DOM, either by changing its structure or by altering the appearance and behavior of existing elements.
- Types of Directives:
- Component Directives: These are directives with a template. As discussed, components are the most common type of directive. They control a specific view on the screen.
- Example:
AppComponent,UserListComponent. - Syntax:
@Component({ selector: 'app-root', template:…
}) class AppComponent {}
- Example:
- Structural Directives: These directives change the DOM layout by adding, removing, or manipulating elements and their subtrees. They are identified by a leading asterisk (
*).- Example:
*ngIf,*ngFor,*ngSwitchCase. - Use Case: Conditionally rendering an element (
*ngIf), iterating over a collection (*ngFor). - Mechanism: Structural directives typically work with an
<ng-template>internally. For instance,*ngIf="condition"is syntactic sugar for<ng-template [ngIf]="condition">...</ng-template>.
- Example:
- Attribute Directives: These directives change the appearance or behavior of an existing DOM element, component, or another directive. They are applied as attributes to HTML elements.
- Example:
ngClass,ngStyle,ngModel. - Use Case: Highlighting an element, changing its style, adding tooltip behavior.
- Mechanism: They interact with the host element using
ElementRefandRenderer2(or directly withHostBinding/HostListener).
- Example:
- Component Directives: These are directives with a template. As discussed, components are the most common type of directive. They control a specific view on the screen.
- Types of Directives:
- Key Points:
- All directives are classes decorated with
@Directive(). Components are special directives with@Component(). - Structural directives manipulate the DOM structure (
*). - Attribute directives modify element appearance/behavior (no
*).
- All directives are classes decorated with
- Common Mistakes:
- Confusing
*ngIf(structural) with[ngClass](attribute). - Trying to apply structural directive syntax (
*) to an attribute directive. - Not understanding that structural directives operate on
<ng-template>.
- Confusing
- Follow-up: When would you create a custom attribute directive versus a custom structural directive?
7. How do you create a custom Structural Directive? Provide an example.
Q: Describe the process of creating a custom Structural Directive in Angular. Provide a practical example, like an
*ngIfRoledirective that only renders content if the user has a specific role.A: Creating a custom structural directive involves using
TemplateRefandViewContainerRefto control the rendering of a template.- Process:
- Define the Directive: Create a class and decorate it with
@Directive(), providing aselector. - Inject Dependencies: Inject
TemplateRef<any>andViewContainerRefinto the constructor.TemplateRef: Represents the<ng-template>that the structural directive is attached to (the content to be rendered).ViewContainerRef: Represents the container where the template view can be embedded or removed.
- Implement Input Property: Define an
@Input()property with the same name as the directive’s selector (e.g.,ngIfRole). This property will receive the condition for rendering. - Control View: Inside the setter of the input property, use
ViewContainerRef.createEmbeddedView(this.templateRef)to render the content orViewContainerRef.clear()to remove it based on the condition.
- Define the Directive: Create a class and decorate it with
Example:
*ngIfRoleDirective (Angular v13-v21) This directive will only render the content if the user has one of the specified roles.// src/app/directives/if-role.directive.ts import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core'; import { AuthService } from '../services/auth.service'; // Assume an AuthService exists @Directive({ selector: '[ngIfRole]', standalone: true // For Angular v14+ }) export class IfRoleDirective { private hasView = false; constructor( private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef, private authService: AuthService // Inject a service to get user roles ) {} @Input() set ngIfRole(allowedRoles: string[]) { const userRoles = this.authService.getUserRoles(); // Get current user roles // Check if the user has any of the allowed roles const hasPermission = userRoles.some(role => allowedRoles.includes(role)); if (hasPermission && !this.hasView) { this.viewContainer.createEmbeddedView(this.templateRef); this.hasView = true; } else if (!hasPermission && this.hasView) { this.viewContainer.clear(); this.hasView = false; } } }Usage:
<!-- app.component.html --> <button *ngIfRole="['admin', 'editor']">Edit Content</button> <p *ngIfRole="['admin']">Admin Dashboard Access</p> <div *ngIfRole="['user']">Regular User Content</div>- Process:
Key Points:
- Requires
TemplateRef(what to render) andViewContainerRef(where to render). - The input property setter is where the rendering logic typically resides.
createEmbeddedView()inserts the template,clear()removes it.
- Requires
Common Mistakes:
- Forgetting to inject
TemplateRefandViewContainerRef. - Not using the input setter to trigger rendering logic.
- Incorrectly handling the
hasViewflag, leading to multiple views or no view.
- Forgetting to inject
Follow-up: How would you handle an
elseblock for a custom structural directive, similar to*ngIf="condition; else otherBlock"?
8. What are Pipes in Angular? Give examples of built-in pipes.
- Q: What are Pipes in Angular? Describe their purpose and provide at least three examples of commonly used built-in pipes.
- A: Pipes are simple functions that transform data directly within your Angular templates. They take data as input and transform it into a desired output format, making your templates cleaner and more readable by separating presentation logic from component logic.
- Purpose:
- Data Transformation: Format dates, currencies, numbers, strings, etc.
- Readability: Keep component logic focused on business rules, not presentation.
- Reusability: Apply the same transformation consistently across multiple templates.
- Syntax:
{{ value | pipeName[:param1[:param2]] }} - Examples of Built-in Pipes:
DatePipe: Formats a date value according to locale rules.{{ birthday | date }}// ‘Dec 23, 2025’{{ birthday | date:'shortTime' }}// ‘12:00 PM’{{ birthday | date:'fullDate' }}// ‘Tuesday, December 23, 2025’
CurrencyPipe: Transforms a number to a currency string, formatted according to locale rules.{{ price | currency }}// ‘$1,234.56’ (default locale){{ price | currency:'EUR':'symbol':'1.2-2' }}// ‘€1,234.56’
DecimalPipe/PercentPipe: Formats a number with decimal points or as a percentage.{{ pi | number:'1.0-2' }}// ‘3.14’ (min 1 integer digit, min 0 max 2 fractional digits){{ 0.75 | percent }}// ‘75%’
JsonPipe: Converts a JavaScript value to a JSON string. Useful for debugging.{{ user | json }}//{"name": "Alice", "age": 30}
AsyncPipe: Subscribes to an Observable or Promise and returns its latest value. It automatically unsubscribes when the component is destroyed, preventing memory leaks.{{ data$ | async }}(wheredata$is an Observable)
- Purpose:
- Key Points:
- Used for display-only transformations.
- Enhance template readability.
AsyncPipeis crucial for reactive programming in templates.
- Common Mistakes:
- Using pipes for complex logic that should reside in the component class.
- Misunderstanding
AsyncPipe’s role in subscription management. - Not providing correct format parameters to pipes.
- Follow-up: What’s the difference between a pipe and a method call in a template? When should you prefer one over the other?
9. Explain Pure vs. Impure Pipes and when to use each.
- Q: Explain the concepts of Pure and Impure Pipes in Angular. When would you choose one over the other, and what are the performance implications?
- A: Angular pipes are categorized as either pure or impure, which dictates how Angular’s change detection mechanism processes them.
- Pure Pipes:
- Behavior: A pure pipe is executed only when its input value (or any of its arguments) changes. Angular performs a shallow check for primitive inputs and a reference check for object/array inputs.
- Default: All built-in pipes are pure by default (e.g.,
DatePipe,CurrencyPipe). Custom pipes are also pure by default. - Performance: Highly efficient. Since they only re-execute on input changes, they don’t impact performance during every change detection cycle.
- Use Case: Ideal for transformations that produce the same output for the same input and do not rely on internal state or external factors (like
DatePipeorUpperCasePipe).
- Impure Pipes:
- Behavior: An impure pipe is executed during every change detection cycle, regardless of whether its input value has changed.
- Declaration: To make a custom pipe impure, set
pure: falsein the@Pipe()decorator:@Pipe({ name: 'myImpurePipe', pure: false }). - Performance: Can significantly impact performance if they perform complex calculations, as they run very frequently. Use with caution.
- Use Case: Necessary when the output depends on mutable data, internal component state, or external factors that Angular’s change detection won’t automatically detect (e.g., a pipe that filters an array based on a component’s internal filter criteria, or a pipe that uses
Date.now()to update a “time ago” display). TheAsyncPipeis a built-in impure pipe because its output depends on an external Observable’s emission, not just its input reference.
- Pure Pipes:
- Key Points:
- Pure pipes are default and performant; only run on input reference change.
- Impure pipes run on every change detection cycle; can be a performance bottleneck.
- Prefer pure pipes whenever possible.
AsyncPipeis a common example of a useful impure pipe.
- Common Mistakes:
- Making a pipe impure unnecessarily, leading to performance degradation.
- Expecting a pure pipe to detect changes within mutable objects/arrays.
- Using impure pipes for complex filtering/sorting without considering alternatives (e.g., performing filtering in the component and passing the filtered array to a pure pipe).
- Follow-up: Can you describe a scenario where using an impure pipe would be justified despite its performance implications?
10. How do you create a custom Pipe?
Q: Describe the steps to create a custom pipe in Angular. Provide an example of a pipe that truncates a string and adds an ellipsis.
A: Creating a custom pipe involves defining a class that implements the
PipeTransforminterface and decorating it with@Pipe().- Steps:
- Create a Pipe Class: Define a TypeScript class.
- Implement
PipeTransform: Implement thePipeTransforminterface, which requires atransform()method. - Implement
transform()method: This method will contain the logic for your data transformation. It takes the inputvalueas its first argument and any additional parameters as subsequent arguments. - Decorate with
@Pipe(): Use the@Pipe()decorator to register your class as a pipe. Provide anameproperty for the pipe’s selector. - Register the Pipe:
- For non-standalone components (Angular < v14 or using
NgModule): Declare the pipe in thedeclarationsarray of anNgModule(e.g.,AppModule). - For standalone components (Angular v14+): Set
standalone: truein the@Pipe()decorator and import it directly into theimportsarray of the component or another standalone pipe/directive.
- For non-standalone components (Angular < v14 or using
Example:
TruncatePipe(Angular v13-v21) This pipe truncates a string to a specified length and adds ‘…’ at the end.// src/app/pipes/truncate.pipe.ts import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'truncate', standalone: true // For Angular v14+ }) export class TruncatePipe implements PipeTransform { transform(value: string, limit: number = 50, ellipsis: string = '...'): string { if (!value) { return ''; } if (value.length <= limit) { return value; } return value.substring(0, limit) + ellipsis; } }Usage:
<!-- app.component.html --> <p>{{ longText | truncate: 10 }}</p> <!-- Output: "This is a..." --> <p>{{ anotherText | truncate: 20 : ' READ MORE' }}</p> <!-- Output: "This is another text READ MORE" -->- Steps:
Key Points:
- Must implement
PipeTransform. transformmethod defines the logic.@Pipe()decorator with a uniquename.- Remember to declare/import the pipe.
- Must implement
Common Mistakes:
- Forgetting to implement
PipeTransformor itstransformmethod. - Not providing a unique
namein the@Pipe()decorator. - Incorrectly handling default values for pipe parameters.
- Forgetting to implement
Follow-up: How would you unit test this
TruncatePipe?
11. Discuss common component communication strategies beyond @Input() and @Output().
- Q: Beyond
@Input()and@Output(), what are other common strategies for component communication in Angular, especially for complex or unrelated components? - A: While
@Input()and@Output()are excellent for parent-child communication, Angular offers several other patterns for more complex scenarios:- Service (Shared Service Pattern):
- Mechanism: Create an injectable service (using
@Injectable({ providedIn: 'root' })for a singleton instance across the app). Components inject this service and use it to share data or trigger actions via properties, methods, or Observables (e.g.,Subject,BehaviorSubject). - Use Case: Communication between unrelated components, global state management (e.g., user authentication status, shared data models), cross-component events.
- Mechanism: Create an injectable service (using
ViewChild/ViewChildren(Parent-to-Child, Direct Access):- Mechanism: A parent component can get a reference to a child component or a DOM element in its own view using
@ViewChild()(for a single instance) or@ViewChildren()(for a collection). This allows the parent to directly call methods or access properties of the child. - Use Case: Calling a specific method on a child component (e.g.,
childForm.submit(),childModal.open()), accessing a native DOM element. - Caution: Breaks encapsulation; use sparingly. Access should typically be done after
ngAfterViewInit.
- Mechanism: A parent component can get a reference to a child component or a DOM element in its own view using
ContentChild/ContentChildren(Parent-to-Child, Projected Content):- Mechanism: Similar to
ViewChild, but used when a component needs to query content that has been projected into it viang-content. - Use Case: A
TabsComponentquerying forTabPanelComponentchildren projected into it to manage their visibility. - Caution: Similar to
ViewChild, breaks encapsulation; access afterngAfterContentInit.
- Mechanism: Similar to
- Router (URL Parameters, Query Parameters, State):
- Mechanism: Pass data between routed components using URL parameters (
/users/:id), query parameters (/search?query=angular), or router state (history.state). - Use Case: Navigating to a detail page with an ID, filtering results based on URL parameters, passing small, non-sensitive data during navigation.
- Mechanism: Pass data between routed components using URL parameters (
- State Management Libraries (Ngrx, NgRx Signals, Akita, Elf):
- Mechanism: For large-scale applications, dedicated state management libraries provide a centralized store for application state. Components dispatch actions to modify the state, and subscribe to state changes.
- Use Case: Complex, highly reactive applications with many components sharing global state, ensuring predictable state changes, debugging.
- Modern Trend (2025): NgRx Signals is gaining traction as a more modern, signal-based approach to state management, offering improved performance and developer experience compared to traditional NgRx.
- Service (Shared Service Pattern):
- Key Points:
- Choose the simplest method first (Input/Output).
- Services are great for unrelated components and global state.
ViewChild/ContentChildfor direct interaction (use with care).- Router for navigation-related data.
- State management libraries for large, complex apps.
- Common Mistakes:
- Overusing
ViewChildfor general communication, leading to tightly coupled components. - Creating multiple instances of a service when a singleton is needed (by not providing
providedIn: 'root'). - Not unsubscribing from service Observables, causing memory leaks.
- Overusing
- Follow-up: When would you consider introducing a state management library like NgRx into an Angular project?
12. How does Angular handle DOM manipulation safely, especially with custom directives?
- Q: How does Angular handle DOM manipulation safely, especially when creating custom directives? Discuss
ElementRef,Renderer2, andDomSanitizer. - A: Direct DOM manipulation in web applications can lead to security vulnerabilities (like XSS attacks) and can bypass Angular’s change detection mechanism, leading to unpredictable behavior. Angular provides several abstractions to handle DOM interaction safely and efficiently.
ElementRef:- Purpose: A wrapper around the host element of a component or directive. It provides direct access to the underlying native DOM element (
nativeElement). - Safety: While it provides direct access, using
nativeElementdirectly is generally discouraged, especially for modifying the DOM, as it can expose the application to XSS attacks and bypass Angular’s rendering pipeline. It’s primarily used for accessing properties or calling methods on the native element thatRenderer2doesn’t cover (e.g., focusing an input).
- Purpose: A wrapper around the host element of a component or directive. It provides direct access to the underlying native DOM element (
Renderer2(Preferred for DOM Manipulation):- Purpose: An abstraction layer that allows you to manipulate the DOM in a platform-agnostic and secure way. It’s the recommended way to interact with the DOM within directives and components.
- Safety:
Renderer2sanitizes values and ensures that DOM modifications are performed in a way that is compatible with different rendering environments (e.g., browser, web worker, server-side rendering). It prevents direct access tonativeElement, forcing you to use its methods for operations likesetStyle(),addClass(),appendChild(),listen(). - Use Case: Creating custom attribute directives to add/remove classes, styles, or attributes dynamically.
DomSanitizer:- Purpose: Helps protect against Cross-Site Scripting (XSS) attacks by sanitizing untrusted values (HTML, styles, URLs, resources) that might be directly inserted into the DOM.
- Mechanism: Angular automatically sanitizes most values bound into the DOM. However, if you must bind an untrusted value (e.g., dynamic HTML from an API) that Angular would normally block, you can explicitly tell Angular to trust it using
DomSanitizer.bypassSecurityTrustHtml(),bypassSecurityTrustStyle(), etc. - Caution: Bypassing sanitization should only be done when you are absolutely certain the source of the value is safe, as it opens up a potential XSS vulnerability.
HostBindingandHostListener(Declarative DOM Interaction):- Purpose: Decorators that provide a declarative way for a directive to interact with its host element.
@HostBinding('property'): Binds a host element property to a directive/component property. (e.g.,@HostBinding('class.active') isActive: boolean;).@HostListener('event'): Subscribes to events of the host element. (e.g.,@HostListener('click') onClick() { ... }).- Safety: These decorators use
Renderer2internally, ensuring safe DOM interaction.
- Key Points:
- Avoid direct
nativeElementmanipulation withElementRefunless absolutely necessary. Renderer2is the secure, platform-agnostic way to modify the DOM.DomSanitizeris for explicitly trusting otherwise untrusted content (use with extreme caution).HostBinding/HostListeneroffer a clean, declarative approach for common host interactions.
- Avoid direct
- Common Mistakes:
- Directly manipulating
ElementRef.nativeElementfor style or structure changes. - Bypassing
DomSanitizerwithout fully understanding the security implications. - Not using
Renderer2when programmatic DOM changes are required.
- Directly manipulating
- Follow-up: Can you describe a scenario where you would absolutely need to use
DomSanitizer.bypassSecurityTrustHtml()?
MCQ Section
Choose the best answer for each question.
1. Which lifecycle hook is called only once after Angular initializes the component’s data-bound input properties and sets up the component?
A) ngOnChanges
B) ngDoCheck
C) ngOnInit
D) ngAfterViewInit
* **Correct Answer:** C
* **Explanation:**
* A) `ngOnChanges` is called before `ngOnInit` and whenever input properties change.
* B) `ngDoCheck` is called after `ngOnChanges` and `ngOnInit`, and then frequently during change detection.
* C) `ngOnInit` is the standard hook for component initialization logic after inputs are set.
* D) `ngAfterViewInit` is called after the component's view and child views are initialized.
2. A component uses ChangeDetectionStrategy.OnPush. If an @Input() property user (an object) is mutated (e.g., this.user.name = 'New Name';) instead of being replaced with a new object reference, what will happen?
A) The component will automatically detect the change and update its view.
B) The component will only update its view if ChangeDetectorRef.detectChanges() is manually called.
C) The component will not update its view because OnPush only checks for reference changes.
D) An error will be thrown because mutation is not allowed with OnPush.
* **Correct Answer:** C
* **Explanation:** `OnPush` change detection relies on *reference equality* for input objects. If an object is mutated without its reference changing, Angular will not detect the change, and the component's view will not update unless manually forced.
3. Which of the following is a Structural Directive in Angular?
A) ngClass
B) ngStyle
C) ngIf
D) ngModel
* **Correct Answer:** C
* **Explanation:**
* A) `ngClass` is an Attribute Directive (modifies element's classes).
* B) `ngStyle` is an Attribute Directive (modifies element's styles).
* C) `ngIf` is a Structural Directive (adds/removes elements from the DOM).
* D) `ngModel` is an Attribute Directive (enables two-way data binding).
4. When creating a custom structural directive, which two services are typically injected to control the rendering of content?
A) ElementRef and Renderer2
B) TemplateRef and ViewContainerRef
C) ChangeDetectorRef and NgZone
D) HttpClient and Router
* **Correct Answer:** B
* **Explanation:** `TemplateRef` represents the template to be rendered, and `ViewContainerRef` is where the template will be embedded or removed from the DOM.
5. What is the primary benefit of using the async pipe in Angular templates with Observables?
A) It automatically converts an Observable to a Promise.
B) It ensures the component’s view is always updated on every change detection cycle.
C) It automatically subscribes to the Observable and unsubscribes when the component is destroyed.
D) It allows for direct manipulation of the DOM elements that display the Observable’s data.
* **Correct Answer:** C
* **Explanation:** The `async` pipe handles the subscription and unsubscription lifecycle of Observables and Promises, preventing memory leaks and simplifying template logic.
6. Which type of pipe is executed during every change detection cycle, regardless of whether its input value has changed? A) Pure Pipe B) Impure Pipe C) Async Pipe (only when it emits) D) Built-in Pipe
* **Correct Answer:** B
* **Explanation:** Impure pipes (like `AsyncPipe` internally) are designed to run on every change detection cycle. Pure pipes only run when their input reference changes.
7. In Angular v14 and later, what property is set in the @Component() or @Directive() decorator to indicate that it doesn’t need to be declared in an NgModule?
A) moduleless: true
B) isolated: true
C) standalone: true
D) independent: true
* **Correct Answer:** C
* **Explanation:** The `standalone: true` flag was introduced in Angular v14 (stable in v15) to enable standalone components, directives, and pipes, simplifying module organization.
Mock Interview Scenario: Refactoring a Product Display for Reusability and Performance
Scenario Setup:
You are interviewing for a Senior Frontend Developer position. The interviewer presents you with a legacy Angular (v13) application’s ProductDisplayComponent. This component currently handles displaying product details, managing a “favorite” state, and has some hardcoded rendering logic. Your task is to discuss how you would refactor this component to adhere to modern Angular (v17+) best practices, focusing on reusability, performance, and maintainability using custom directives and pipes where appropriate.
Interviewer: “Alright, we have this ProductDisplayComponent in an older part of our application. It’s quite monolithic. Let’s imagine you’re tasked with modernizing it. Here’s a simplified version of its template and class:”
// product-display.component.ts (Simplified v13 example)
import { Component, Input, OnInit } from '@angular/core';
import { Product } from '../models/product.model'; // Assume Product interface
@Component({
selector: 'app-product-display',
template: `
<div class="product-card">
<img [src]="product.imageUrl" alt="{{ product.name }}">
<h2>{{ product.name }}</h2>
<p class="price">{{ product.price | currency }}</p>
<p *ngIf="product.description && product.description.length > 100" class="description">
{{ product.description.substring(0, 100) }}...
<a href="#" (click)="showFullDescription = !showFullDescription">
{{ showFullDescription ? 'Show Less' : 'Show More' }}
</a>
</p>
<p *ngIf="product.description && product.description.length <= 100" class="description">
{{ product.description }}
</p>
<button (click)="toggleFavorite()">
{{ isFavorite ? 'Remove from Favorites' : 'Add to Favorites' }}
</button>
<div *ngIf="product.availableStock < 5" class="stock-warning">
Low Stock! Only {{ product.availableStock }} left.
</div>
<div *ngIf="!product.availableStock || product.availableStock === 0" class="out-of-stock">
Out of Stock
</div>
</div>
`,
styles: [`
.product-card { border: 1px solid #ccc; padding: 15px; margin: 10px; }
.price { font-weight: bold; color: green; }
.stock-warning { color: orange; }
.out-of-stock { color: red; font-weight: bold; }
`]
})
export class ProductDisplayComponent implements OnInit {
@Input() product: Product;
isFavorite: boolean = false;
showFullDescription: boolean = false;
constructor(/* private favoriteService: FavoriteService */) {} // Assume FavoriteService
ngOnInit() {
// this.isFavorite = this.favoriteService.checkFavorite(this.product.id);
}
toggleFavorite() {
this.isFavorite = !this.isFavorite;
// this.favoriteService.updateFavorite(this.product.id, this.isFavorite);
}
}
Interviewer Questions & Expected Flow:
Initial Assessment & Strategy:
- Interviewer: “Looking at this component, what are your initial thoughts on areas for improvement in terms of reusability, maintainability, and performance, considering modern Angular (v17+)?”
- Expected Answer:
- Decomposition: Too much logic and UI in one component. Break it down into smaller, focused components (e.g.,
ProductImageComponent,ProductPriceComponent,FavoriteButtonComponent,StockStatusComponent). - Change Detection: Default change detection is likely inefficient. Suggest
ChangeDetectionStrategy.OnPushforProductDisplayComponentand its children. - Pipes: The description truncation and currency formatting are good candidates for pipes.
- Directives: Stock status logic (
*ngIfconditions) can be abstracted into a custom attribute or structural directive. - Standalone Components (v14+): Mention converting to standalone for better modularity and tree-shaking.
- Signals (v17+): For the
isFavoritestate, consider using signals for more granular reactivity.
- Decomposition: Too much logic and UI in one component. Break it down into smaller, focused components (e.g.,
Custom Pipe Implementation:
- Interviewer: “You mentioned the description truncation. How would you implement that as a custom pipe, and why is that a better approach than the current
substringlogic in the template?” - Expected Answer:
- Why Pipe: Separates presentation logic from component logic, reusability across other components, cleaner template.
- Pipe Definition: Create
TruncatePipe(as shown in Q10 above) implementingPipeTransform, withvalue,limit, andellipsisparameters. - Usage:
{{ product.description | truncate:100 }} - Pure Pipe: Emphasize that it should be a pure pipe as its output only depends on its input string and parameters.
- Interviewer: “You mentioned the description truncation. How would you implement that as a custom pipe, and why is that a better approach than the current
Custom Directive for Stock Status:
- Interviewer: “The stock warning logic (
*ngIf="product.availableStock < 5") is repetitive and tightly coupled. How would you abstract this into a custom directive to make it more reusable and declarative?” - Expected Answer:
- Directive Type: Suggest an Attribute Directive (
[appStockStatus]) if we want to add classes/styles to an existing element, or a Structural Directive (*appStockStatus) if we want to conditionally render the entire stock message. A structural directive like*appStockStatus="product.availableStock"seems more flexible here. - Structural Directive Design:
- Selector:
*appStockStatus @Input() appStockStatus: number;- Inject
TemplateRefandViewContainerRef. - Logic in
ngOnChangesor input setter:- If
availableStockis 0, render ‘Out of Stock’ template. - If
availableStockis < 5, render ‘Low Stock’ template. - Else, clear the view.
- If
- Alternative (Attribute Directive): If the requirement was to style the text, an attribute directive could bind classes:
[appStockStatus]="product.availableStock".
- Selector:
- Example (Conceptual Structural):(The directive itself would manage which specific template to render based on stock, or it could be simpler and just render if stock is low/out.)
<!-- In ProductDisplayComponent's refactored template --> <ng-container *appStockStatus="product.availableStock"> <!-- Content for low/out of stock will be projected here --> </ng-container> - Example (Attribute Directive for styling):
// stock-status.directive.ts @Directive({ selector: '[appStockStatus]', standalone: true }) export class StockStatusDirective { constructor(private el: ElementRef, private renderer: Renderer2) {} @Input() set appStockStatus(stock: number) { this.renderer.removeClass(this.el.nativeElement, 'low-stock'); this.renderer.removeClass(this.el.nativeElement, 'out-of-stock'); if (stock === 0) { this.renderer.addClass(this.el.nativeElement, 'out-of-stock'); } else if (stock < 5) { this.renderer.addClass(this.el.nativeElement, 'low-stock'); } } }<!-- Usage --> <p [appStockStatus]="product.availableStock">Stock: {{ product.availableStock }}</p>
- Directive Type: Suggest an Attribute Directive (
- Interviewer: “The stock warning logic (
Component Decomposition & Communication:
- Interviewer: “Let’s focus on the
FavoriteButtonComponent. How would you extract this into its own component, and what would be the communication pattern between this new component and theProductDisplayComponent?” - Expected Answer:
- New Component: Create
FavoriteButtonComponent(standalone). @Input(): It would need to receive theisFavoritestate and potentially theproductIdfromProductDisplayComponent.// favorite-button.component.ts @Input() isFavorite: boolean = false; @Input() productId: string; // Or Product ID@Output(): It would emit an event when the favorite status changes.// favorite-button.component.ts @Output() favoriteToggled = new EventEmitter<boolean>(); // ... (click)="favoriteToggled.emit(!isFavorite)"- Usage in
ProductDisplayComponent:<app-favorite-button [isFavorite]="isFavorite" [productId]="product.id" (favoriteToggled)="onFavoriteToggled($event)"> </app-favorite-button> - Service: Mention that
FavoriteServicewould ideally be injected intoFavoriteButtonComponentto handle the actual API calls, separating concerns.
- New Component: Create
- Interviewer: “Let’s focus on the
Performance Considerations (Change Detection):
- Interviewer: “Given these changes, how would you ensure optimal change detection performance for the
ProductDisplayComponentand its children?” - Expected Answer:
ChangeDetectionStrategy.OnPush: ApplyChangeDetectionStrategy.OnPushtoProductDisplayComponentand all its newly created child components (e.g.,FavoriteButtonComponent).- Immutability: Emphasize that
productinput should be treated immutably. Ifproductdetails change, a newProductobject reference should be passed down. - Async Pipe: If
productdata comes from an Observable, use theasyncpipe in the template to automatically handle subscriptions and trigger change detection. - Signals (v17+): For the
isFavoritestate, if converted to a signal (isFavorite = signal(false)), updates to this signal would directly trigger re-renders only whereisFavorite()is used, providing highly optimized change detection for that specific piece of state.
- Interviewer: “Given these changes, how would you ensure optimal change detection performance for the
Red Flags to Avoid:
- Suggesting direct DOM manipulation with
ElementRef.nativeElement. - Not considering
OnPushfor performance. - Creating an overly complex solution when a simpler pipe or directive would suffice.
- Not discussing how components communicate after decomposition.
- Ignoring memory leak possibilities (e.g., not unsubscribing from Observables if not using
asyncpipe).
Practical Tips
- Hands-on Practice: The best way to understand Components, Directives, and Pipes is to build them. Create small, isolated Angular projects to experiment with custom implementations. Try building:
- A custom
*ngIfAuthstructural directive. - A
HighlightDirectiveattribute directive. - A
TimeAgoPipeor aSearchFilterPipe.
- A custom
- Understand the “Why”: Don’t just memorize definitions. Understand why a particular pattern (e.g.,
OnPush, services for communication) is recommended. This helps you apply concepts to novel problems. - Read Official Documentation: The Angular documentation is the most authoritative and up-to-date resource. Pay special attention to the “Fundamentals” and “Techniques” sections for components, directives, and pipes.
- Explore Source Code (Optional but Recommended): For advanced learners, looking at the source code of Angular’s built-in directives (e.g.,
ngIf,ngFor) can provide deep insights into how they leverageTemplateRefandViewContainerRef. - Focus on Reusability and Modularity: When designing components, directives, or pipes, always think about how they can be made generic and reusable across different parts of an application or even in other projects.
- Performance Awareness: Always consider the performance implications, especially for pipes (pure vs. impure) and change detection (
OnPushstrategy). Explain your choices when asked. - Stay Updated: Angular evolves rapidly. Regularly check for new features and best practices (e.g., standalone components, signals, functional interceptors, etc.) on the official blog and reputable community resources.
Summary
This chapter has provided a deep dive into the core Angular concepts of Components, Directives, and Pipes, essential for any Angular developer. We’ve covered their fundamental definitions, lifecycle management, communication patterns, and practical applications, including the creation of custom solutions. Understanding the nuances of ChangeDetectionStrategy.OnPush, the differences between structural and attribute directives, and the power of pure vs. impure pipes is critical for building high-performance, maintainable Angular applications.
As you continue your interview preparation, remember that theoretical knowledge combined with practical implementation experience is key. Practice explaining these concepts clearly, providing real-world examples, and discussing trade-offs. The mock interview scenario highlighted how these concepts are tested in a practical context, emphasizing problem-solving and architectural thinking.
Your next step should be to continue practicing, perhaps by implementing some of the custom directives and pipes discussed, and then moving on to understanding Angular Services, Dependency Injection, and Routing, which will be covered in the next chapter.
This interview preparation guide is AI-assisted and reviewed. It references official documentation and recognized interview preparation resources.