Angular interview questions and answers for 2025
Angular Interview Questions for Freshers and Intermediate Levels
What is Angular, and how is it different from AngularJS?
Angular is a modern, TypeScript-based front-end web application framework developed by Google. It builds on the concepts introduced in AngularJS but was rewritten from the ground up. Key differences include:
- Architecture: Angular uses a component-based architecture, whereas AngularJS followed an MVC/MVVM approach.
- Language: Angular leverages TypeScript, a statically-typed superset of JavaScript, improving code reliability and maintainability.
- Performance: Angular provides enhanced performance due to Ahead-Of-Time (AOT) compilation, tree shaking, and a faster change detection mechanism via Zone.js.
- Mobile support: Angular is mobile-first, focusing on performance and modularity.
What are Angular Components?
Components are the fundamental building blocks of an Angular application. They:
- Contain logic (TypeScript class), a view (HTML template), and styling (CSS/SCSS).
- Are defined using the
@Component
decorator. - Represent a piece of the user interface, such as a header, footer, or a form element.
Example:
import { Component } from '@angular/core';
@Component({
selector: 'app-hello',
template: `<h1>Hello, Angular!</h1>`,
styles: [`h1 { color: blue; }`]
})
export class HelloComponent { }
Explain the role of Modules in Angular.
Modules in Angular organize related components, directives, pipes, and services into functional units. The root module (often AppModule
) bootstraps the application, while feature modules group code into cohesive blocks. Modules improve maintainability and allow for code splitting and lazy loading.
Example (AppModule):
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { HelloComponent } from './hello.component';
@NgModule({
declarations: [AppComponent, HelloComponent],
imports: [BrowserModule],
bootstrap: [AppComponent]
})
export class AppModule {}
What are Directives in Angular?
Directives are classes that add behavior to elements in Angular templates. There are three types:
- Component Directives: Directives with a template.
- Structural Directives: Modify the structure of the DOM (e.g.,
ngIf
,ngFor
). - Attribute Directives: Change the appearance or behavior of an element (e.g.,
ngClass
,ngStyle
).
Explain Data Binding in Angular.
Data binding synchronizes data between the model (TypeScript class) and the view (HTML template). The four main types are:
- Interpolation ({{ }}): One-way binding from component to view.
- Property Binding ([ ]): One-way binding from component property to a DOM property.
- Event Binding (( )): One-way binding from view to component via event handlers.
- Two-Way Binding ([()]): Combination of property and event binding, using
ngModel
.
Example:
<!-- Interpolation -->
<p>{{message}}</p>
<!-- Property Binding -->
<img [src]="imageUrl">
<!-- Event Binding -->
<button (click)="onClick()">Click me</button>
<!-- Two-Way Binding -->
<input [(ngModel)]="username">
What are Pipes in Angular?
Pipes transform displayed data in the template without altering the data source. Angular provides built-in pipes (e.g., date
, currency
, percent
), and developers can create custom pipes.
Example (using a built-in pipe):
<p>{{ today | date:'fullDate' }}</p>
Describe Angular’s Lifecycle Hooks.
Lifecycle hooks are methods that Angular calls at specific stages of a component’s creation, update, and destruction. Common hooks include:
ngOnInit()
: Called once after component initialization.ngOnChanges()
: Called when input properties change.ngOnDestroy()
: Called just before component is destroyed.ngDoCheck()
: Detects changes not picked up by default change detection.
What is Dependency Injection (DI) in Angular?
DI is a design pattern that provides dependencies to components, directives, and services at runtime. Angular’s built-in DI container resolves and instantiates services, making them available to classes that need them. This results in more modular, testable, and maintainable code.
How do you create and use Services in Angular?
Services are classes that contain business logic or data fetching code. They are injectable and meant to be reused across components.
Example:
import { Injectable } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class DataService {
getData() {
return ['Item1', 'Item2', 'Item3'];
}
}
To use this service in a component:
constructor(private dataService: DataService) {}
ngOnInit() {
const items = this.dataService.getData();
}
What is the difference between Template-Driven and Reactive Forms?
- Template-Driven Forms: Use
ngModel
for two-way data binding. Simple and suitable for smaller forms. Form logic resides in templates. - Reactive Forms: Use
FormControl
andFormGroup
classes. More flexible, scalable, and testable. Form logic resides in TypeScript code.
How do you implement Routing in Angular?
Routing is handled by the Angular Router module. It defines which component to show based on the current URL.
Example:
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './home.component';
import { AboutComponent } from './about.component';
const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'about', component: AboutComponent }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
Explain the concept of Guards in Angular Routing.
Guards are interfaces that allow or restrict navigation. Types of guards include
CanActivate
, CanDeactivate
, Resolve
, CanLoad
, and CanActivateChild
. They return a boolean or observable/promise resolving to boolean.
What is the Async Pipe in Angular?
The Async pipe automatically subscribes to observables and promises, displaying the emitted values and unsubscribing when the component is destroyed. It simplifies async data handling in templates.
Example:
<p>{{ dataObservable$ | async }}</p>
How do you communicate between parent and child components?
Use @Input()
and @Output()
decorators.
@Input()
passes data from parent to child.@Output()
along withEventEmitter
sends events/data from child to parent.
What is Change Detection in Angular?
Change detection determines what parts of the view need updating when data changes. Angular’s default change detection runs after every event, updating the DOM accordingly.
How do you create a Custom Pipe?
Use the @Pipe()
decorator, implement the transform()
method, and then reference it in templates.
Example:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({ name: 'reverse' })
export class ReversePipe implements PipeTransform {
transform(value: string): string {
return value.split('').reverse().join('');
}
}
Explain the concept of ViewEncapsulation in Angular.
ViewEncapsulation controls how styles are applied to components:
Emulated
(default): Styles are scoped to the component by emulating Shadow DOM.ShadowDom
: Uses real Shadow DOM.None
: Styles are applied globally without encapsulation.
What is the Angular CLI?
The Angular CLI is a command-line tool that helps create, develop, build, and maintain Angular applications. It provides scaffolding features and simplifies many common tasks like generating components, services, and modules.
Example:
ng new my-app
ng generate component my-component
ng serve
What is AOT Compilation in Angular?
Ahead-Of-Time (AOT) compilation pre-compiles templates and components at build time, resulting in smaller bundles and faster runtime performance.
How do you handle HTTP requests in Angular?
Use Angular’s HttpClient
service to perform HTTP calls. Import HttpClientModule
in AppModule
and inject HttpClient
into a service or component.
Example:
this.http.get('<https://api.example.com/data>').subscribe(data => console.log(data));
What is NgZone in Angular?
NgZone
helps Angular detect changes outside the normal Angular zone (like DOM events or timers) and trigger change detection manually if needed.
Explain Structural vs. Attribute Directives with examples.
- Structural Directive: Changes the DOM structure. E.g.,
ngIf
,ngFor
. - Attribute Directive: Changes the appearance or behavior of an element. E.g.,
ngClass
,ngStyle
.
What are Angular Animations?
Angular Animations use the Angular animations library (@angular/animations
) to animate transitions between states in components. They offer a declarative API for sophisticated animations.
How do you optimize Angular applications for performance?
- Use AOT compilation.
- Lazy load modules.
- OnPush change detection.
- TrackBy in *ngFor.
- Minimize watchers and unnecessary computations.
- Use memoized selectors (e.g., with NgRx).
What is the purpose of ng-content?
ng-content
is used to project external content into a component’s template, enabling content projection and allowing for more flexible component design.
Example:
<!-- Child component template -->
<ng-content></ng-content>
What is a FormControl in Angular?
FormControl
represents a single form input field’s value and validation status. It’s the basic building block of Reactive Forms.
What is the role of @ViewChild() and @ViewChildren()?
They query the component’s view for a reference to child elements/components.
@ViewChild()
returns the first matching element.@ViewChildren()
returns a query list of all matching elements.
How to share data between unrelated components?
Use a shared service with a BehaviorSubject or Subject to hold data. Both components inject the service and subscribe to changes.
How do you define a custom event from a child component?
Use @Output()
with an EventEmitter
in the child, then bind to that event in the parent template.
Example:
@Output() notify = new EventEmitter<string>();
onClick() {
this.notify.emit('Child clicked!');
}
What is the difference between ngOnInit() and the constructor of a component?
- Constructor: Used for dependency injection initialization. Runs before Angular sets the component’s input properties.
- ngOnInit(): Called once the component’s inputs are set. Ideal for fetching data or initializing logic dependent on input properties.
Angular Interview Questions for Experienced Levels
Can you explain the Angular Change Detection Strategy and the OnPush strategy in detail?
Default change detection runs after every event, updating the view whenever model data changes. OnPush strategy restricts Angular to only check for changes when:
- An @Input() property changes.
- An event originating from the component fires.
- Manually triggered change detection (
markForCheck()
).
This improves performance in large apps by reducing unnecessary checks.
How do you implement Lazy Loading in Angular for performance optimization?
Lazy loading delays the loading of feature modules until needed. Use the loadChildren
property in the route configuration.
Example:
const routes: Routes = [
{ path: 'admin', loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule) }
];
This helps reduce the initial bundle size and improves app load time.
What are Angular Elements, and how might you use them?
Angular Elements allow you to package Angular components as custom elements (web components) that can be used outside Angular. This improves interoperability and allows gradual migration from legacy systems.
Explain the concept of NgModules vs. Standalone Components in Angular (Angular 14+).
Traditionally, Angular requires components to be declared in NgModules. With Angular 14+, standalone components allow bypassing NgModules, simplifying code organization. Standalone components declare their own imports and can be directly bootstrapped, reducing boilerplate and improving code readability.
How would you handle state management in an Angular application?
Options include:
- NgRx: A Redux-inspired state management library that uses actions, reducers, and selectors.
- Akita or NGXS: Alternative state management libraries.
- BehaviorSubjects: For simpler cases, using RxJS BehaviorSubjects within services.
Explain the difference between ViewContainerRef and TemplateRef.
- ViewContainerRef: Represents a container where one or more views can be dynamically attached.
- TemplateRef: Represents an embedded template that can be instantiated multiple times. A TemplateRef is often paired with ViewContainerRef to insert template-based content dynamically.
How do you create a Custom Structural Directive?
Implement the @Directive()
decorator and use ViewContainerRef
and
TemplateRef
to manipulate the DOM.
Example:
@Directive({ selector: '[appIf]' })
export class AppIfDirective {
constructor(private viewContainer: ViewContainerRef, private templateRef: TemplateRef<any>) {}
@Input() set appIf(condition: boolean) {
if (condition) {
this.viewContainer.createEmbeddedView(this.templateRef);
} else {
this.viewContainer.clear();
}
}
}
How do you optimize Angular applications using trackBy with *ngFor?
trackBy
function reduces DOM manipulation by identifying items uniquely, so Angular only updates changed elements instead of re-rendering the entire list.
Example:
<div *ngFor="let item of items; trackBy: trackById">{{ item.name }}</div>
trackById(index: number, item: any) {
return item.id;
}
How do you handle complex forms and custom validators in Reactive Forms?
Create custom validators as functions that return an error object if invalid, or null
if valid. Use Validators.compose()
or directly attach validators to FormControl
s and
FormGroup
s.
Example:
function forbiddenNameValidator(control: FormControl): { [key: string]: any } | null {
const forbidden = control.value === 'admin';
return forbidden ? { 'forbiddenName': { value: control.value } } : null;
}
this.form = new FormGroup({
username: new FormControl('', [forbiddenNameValidator])
});
Can you explain Angular’s Renderer2 and its use cases?
Renderer2
is a service for safely manipulating the DOM. It abstracts away direct DOM access, useful for scenarios like server-side rendering or when running in a web worker, ensuring code is platform-agnostic.
How do you implement custom error handling with the Angular HttpInterceptor?
Create an HttpInterceptor
that catches errors in HTTP responses and handles them globally.
Example:
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).pipe(
catchError(err => {
// Handle error, show notification, etc.
return throwError(err);
})
);
}
}
What are the benefits of using Angular Universal (Server-Side Rendering)?
- Improved SEO: Search engines can index fully-rendered pages.
- Faster initial load: Pre-rendered content means users see meaningful content sooner.
- Better performance on slow networks/devices.
Explain the difference between forRoot() and forChild() methods in Angular Routing.
forRoot()
: Configures the router at the application’s root level, providing the singleton Router service.forChild()
: Configures the router at the feature module level, adding additional routes without creating a new Router instance.
What are Zone.js and its importance in Angular?
Zone.js patches asynchronous APIs and notifies Angular when tasks complete. Angular uses this to trigger change detection automatically without manual checks after asynchronous operations.
How would you create a dynamic component at runtime?
Use ComponentFactoryResolver
or ViewContainerRef.createComponent()
(in newer Angular versions ComponentFactoryResolver
is deprecated in favor of
ViewContainerRef
directly).
Example (Angular 14+):
const componentRef = this.viewContainerRef.createComponent(MyDynamicComponent);
componentRef.instance.someInput = value;
Discuss the pros and cons of OnPush change detection.
Pros:
- Improves performance by reducing change detection cycles.
- Predictable change detection triggered only by inputs or events.
Cons:
- Requires immutable data structures or manual triggering.
- More complexity in application logic.
Explain the concept of Schematics in Angular CLI.
Schematics are templates for scaffolding code (components, modules, services). They enable custom code generation, automating repetitive tasks and maintaining consistency.
How do you integrate Angular with a backend API and handle authentication tokens?
Use HttpClient
interceptors to attach tokens to requests. On successful login, store the token (e.g., in localStorage) and include it in the Authorization header in subsequent requests.
What are TemplateRef and EmbeddedViewRef?
- TemplateRef: Represents an Angular template that can be instantiated.
- EmbeddedViewRef: Represents an instantiated view of a template. Manipulating these references allows dynamic content rendering.
How do you preload lazy-loaded modules?
Use the PreloadingStrategy
interface and configure in the router. Preloading can fetch modules in the background after initial load, improving subsequent navigation speed.
Discuss the importance of content projection and how to implement it.
Content projection allows passing host content into a reusable component’s template. Implemented using ng-content
slots. This fosters component reusability and flexibility.
Example:
<!-- Parent Template -->
<child-component>
<p>Projected Content</p>
</child-component>
<!-- Child Template -->
<ng-content></ng-content>
What is a multi-provider in Angular DI?
A multi-provider allows multiple providers for the same token, aggregating them into an array of values. This is useful for plugins or multiple strategies registered under one token.
Example:
providers: [
{ provide: 'LOGGER', useClass: ConsoleLogger, multi: true },
{ provide: 'LOGGER', useClass: FileLogger, multi: true }
]
How do you approach testing Angular components with dependencies?
Use TestBed to configure a testing module, mock dependencies (services) with Jasmine spies or HttpTestingModule, and then test component logic and DOM rendering.
Explain differential loading in Angular.
Differential loading generates two bundles: one for modern browsers (ES2015+) and one for legacy browsers (ES5). This provides better performance and smaller bundle sizes for modern browsers.
What are Ivy and its benefits in Angular?
Ivy is Angular’s next-generation rendering and compilation engine. Benefits include:
- Faster builds and rebuilds.
- Smaller bundle sizes.
- Improved debugging.
- More flexible compilation that enables future innovation.
How would you implement a custom preloading strategy?
Implement the PreloadingStrategy
interface, decide which modules to preload based on custom logic, and add it to RouterModule.forRoot()
configuration.
Example:
export class CustomPreloadingStrategy implements PreloadingStrategy {
preload(route: Route, load: Function): Observable<any> {
return route.data && route.data.preload ? load() : of(null);
}
}
How do you debug Angular change detection cycles?
- Use
ng.profiler
in the browser console. - Leverage Angular DevTools extension.
- Insert console logs in lifecycle hooks or use
ApplicationRef.tick()
instrumentation. - Switch to OnPush and isolate which components trigger unnecessary checks.
What are RxJS Subjects, and how are they used in Angular?
Subjects are both observables and observers. They are used for:
- Multicasting values to multiple subscribers.
- Emitting events or data streams within services.
- Facilitating communication between unrelated components.
How do you handle memory leaks in Angular?
- Unsubscribe from observables in
ngOnDestroy()
. - Use the
async
pipe for automatic subscription management. - Avoid global event listeners without proper cleanup.
- Use takeUntil pattern with subjects to control subscriptions.
What is the difference between TestBed.createComponent() and fixture.detectChanges() in Angular testing?
TestBed.createComponent()
instantiates the component, creating its fixture.fixture.detectChanges()
triggers change detection, rendering the component’s template and updating the view to reflect component data.
Angular coding tasks suitable for senior-level interviews
Create a Custom Structural Directive
Question:
Write a structural directive *appRepeat
that takes a number and repeats the host element that many times.
// Create a directive that can be used as: *appRepeat="3"
// It should render the host element 3 times in the DOM.
Answer:
import { Directive, Input, ViewContainerRef, TemplateRef } from '@angular/core';
@Directive({
selector: '[appRepeat]'
})
export class RepeatDirective {
@Input() set appRepeat(count: number) {
this.viewContainer.clear();
for (let i = 0; i < count; i++) {
this.viewContainer.createEmbeddedView(this.templateRef);
}
}
constructor(private viewContainer: ViewContainerRef, private templateRef: TemplateRef<any>) {}
}
Explanation:
This directive uses ViewContainerRef
and TemplateRef
to insert multiple copies of the host template into the DOM. By clearing the container and looping, we programmatically create count
instances of the template.
Implement a Custom Pipe with Side Effects
Question:
Write a custom pure: false
pipe called timeLoggerPipe
that logs the current time whenever it transforms a value. The pipe should simply return the input value unchanged but log the time each time change detection runs.
// Create a pipe: {{someValue | timeLoggerPipe}}
// The pipe should log the current time every time it is called.
Answer:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'timeLoggerPipe',
pure: false
})
export class TimeLoggerPipe implements PipeTransform {
transform(value: any): any {
console.log('Time Logger Pipe invoked:', new Date().toISOString());
return value;
}
}
Explanation:
A pure: false
pipe runs on every change detection cycle. By logging inside
transform()
, we track when the pipe is called, demonstrating how impure pipes behave.
Write a Custom Async Validator
Question:
Create a custom asynchronous validator usernameExistsValidator
for a reactive form’s username field that checks if a username already exists by calling a simulated async service.
// Implement an async validator function that returns a promise or observable.
// If the username "admin" is used, it should mark it as invalid.
Answer:
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';
import { of } from 'rxjs';
import { delay, map } from 'rxjs/operators';
export function usernameExistsValidator(): ValidatorFn {
return (control: AbstractControl) => {
const username = control.value;
// Simulate a backend check with delay
return of(username === 'admin').pipe(
delay(500),
map(exists => exists ? { usernameExists: true } : null)
);
};
}
Explanation:
This validator returns an observable that emits after a delay. If the username is ‘admin’, it returns an error object; otherwise, null
. Integrating it into a form control triggers asynchronous validation and updates the form’s validity accordingly.
Create a Component that Dynamically Loads Another Component
Question:
Write an Angular component DynamicHostComponent
that, given a component type as an input, dynamically loads it into a ViewContainerRef
at runtime.
// DynamicHostComponent should have an @Input() componentType: Type<any>.
// On initialization, it should create an instance of that component in its template.
Answer:
import { Component, Input, OnInit, ViewChild, ViewContainerRef, Type } from '@angular/core';
@Component({
selector: 'app-dynamic-host',
template: `<ng-template #dynamicHost></ng-template>`
})
export class DynamicHostComponent implements OnInit {
@ViewChild('dynamicHost', { read: ViewContainerRef, static: true }) vcr!: ViewContainerRef;
@Input() componentType!: Type<any>;
ngOnInit() {
this.vcr.clear();
this.vcr.createComponent(this.componentType);
}
}
Explanation:
This component accepts a component class type and uses createComponent()
to dynamically instantiate it inside a template reference. It leverages ViewContainerRef
for dynamic component creation at runtime.
Create a Route Guard That Depends on a Service
Question:
Implement a CanActivate
guard AuthGuard
that checks if a user is authenticated by calling AuthService.isAuthenticated()
. If not authenticated, it should return false
.
// AuthGuard should implement CanActivate and return boolean based on AuthService.
Answer:
import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { AuthService } from './auth.service';
@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
constructor(private auth: AuthService, private router: Router) {}
canActivate(): boolean {
if (this.auth.isAuthenticated()) {
return true;
}
this.router.navigate(['/login']);
return false;
}
}
Explanation:
AuthGuard
checks authentication state. If the user is not authenticated, it redirects to
/login
and returns false
, preventing route activation.
Create a Directive That Conditionally Applies a CSS Class
Question:
Implement an attribute directive appHighlightOnHover
that adds a CSS class
highlighted
when the user hovers over the element and removes it when the mouse leaves.
// Implement a directive that uses HostListener for mouseenter and mouseleave
// to toggle a class 'highlighted' on the host element.
Answer:
import { Directive, ElementRef, HostListener, Renderer2 } from '@angular/core';
@Directive({
selector: '[appHighlightOnHover]'
})
export class HighlightOnHoverDirective {
constructor(private el: ElementRef, private renderer: Renderer2) {}
@HostListener('mouseenter')
onMouseEnter() {
this.renderer.addClass(this.el.nativeElement, 'highlighted');
}
@HostListener('mouseleave')
onMouseLeave() {
this.renderer.removeClass(this.el.nativeElement, 'highlighted');
}
}
Explanation:
This directive uses Renderer2
to modify classes on mouse events. It showcases attribute directive usage, event listeners, and safe DOM manipulation.
Implement OnPush Change Detection with trackBy
Question:
Create a component ItemListComponent
that uses
ChangeDetectionStrategy.OnPush
and uses *ngFor
with trackBy
to efficiently render a list of items.
// Implement ItemListComponent with OnPush and provide a trackBy function.
// Input: @Input() items: {id: number, name: string}[]
Answer:
import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
@Component({
selector: 'app-item-list',
template: `
<ul>
<li *ngFor="let item of items; trackBy: trackById">{{item.name}}</li>
</ul>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ItemListComponent {
@Input() items: {id: number, name: string}[] = [];
trackById(index: number, item: {id: number, name: string}) {
return item.id;
}
}
Explanation:
By using OnPush, change detection only runs when inputs change. The trackBy
function helps Angular identify unchanged items, reducing re-rendering overhead.
Create a Component Using the Async Pipe
Question:
Build a component DataDisplayComponent
that uses an Observable<string[]>
from a service and displays it using the async
pipe.
// DataDisplayComponent should subscribe to a data$: Observable<string[]>
// from a provided service and display it in a list using async pipe.
Answer:
import { Component } from '@angular/core';
import { DataService } from './data.service';
@Component({
selector: 'app-data-display',
template: `
<ul>
<li *ngFor="let item of dataService.data$ | async">{{item}}</li>
</ul>
`
})
export class DataDisplayComponent {
constructor(public dataService: DataService) {}
}
Explanation:
The async pipe handles subscription and unsubscription automatically, simplifying code. This component displays the live data stream as it updates.
Write Unit Tests for a Service
Question:
Write unit tests for a service CalculatorService
that has a method
add(a: number, b: number): number
. Test that it returns the correct sum.
// Implement a Jasmine test for CalculatorService add method.
// Ensure test uses TestBed and checks correctness.
Answer:
import { TestBed } from '@angular/core/testing';
import { CalculatorService } from './calculator.service';
describe('CalculatorService', () => {
let service: CalculatorService;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [CalculatorService]
});
service = TestBed.inject(CalculatorService);
});
it('should add two numbers correctly', () => {
expect(service.add(2, 3)).toBe(5);
expect(service.add(-1, 1)).toBe(0);
});
});
Explanation:
This test checks the service’s add method using Jasmine’s expect
. TestBed ensures proper Angular testing setup.
Create a Custom HttpInterceptor
Question:
Implement a custom AuthInterceptor
that adds an Authorization
header with a token to every outgoing HTTP request.
// AuthInterceptor should implement HttpInterceptor
// Add a hardcoded token 'Bearer XYZ' to all requests
Answer:
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const authReq = req.clone({ setHeaders: { Authorization: 'Bearer XYZ' }});
return next.handle(authReq);
}
}
Explanation:
This interceptor clones the request and appends the Authorization
header, ensuring all HTTP calls include authentication credentials.
Implement an NgRx Store for a Simple Counter
Question:
Set up a minimal NgRx store for a counter
feature. Implement actions increment
and decrement
, a reducer, and a selector to get the current count.
// Implement actions, reducer, and a selector for a counter store slice.
Answer:
// actions.ts
import { createAction } from '@ngrx/store';
export const increment = createAction('[Counter] Increment');
export const decrement = createAction('[Counter] Decrement');
// reducer.ts
import { createReducer, on } from '@ngrx/store';
import { increment, decrement } from './actions';
export const initialState = 0;
export const counterReducer = createReducer(
initialState,
on(increment, state => state + 1),
on(decrement, state => state - 1)
);
// selectors.ts
import { createSelector } from '@ngrx/store';
export const selectCount = (state: { count: number }) => state.count;
export const getCount = createSelector(selectCount, count => count);
Explanation:
This code defines a simple state slice with actions and a reducer for incrementing/decrementing. The selector returns the current count. This forms the backbone of NgRx state management.
Implement Content Projection in a Component
Question:
Create a CardComponent
that uses ng-content
to allow projecting arbitrary content into it.
// CardComponent should have a template with <ng-content>.
// Parent can place any HTML inside <app-card>...</app-card>.
Answer:
import { Component } from '@angular/core';
@Component({
selector: 'app-card',
template: `
<div class="card">
<ng-content></ng-content>
</div>
`
})
export class CardComponent {}
Explanation:
ng-content
enables the parent component’s content to be projected into the
CardComponent
. This encourages flexible UI composition.
Create a Standalone Component (Angular 14+)
Question:
Write a standalone component HelloStandaloneComponent
with its own imports and bootstrap it without an NgModule.
// Create a standalone component and use bootstrapApplication (in main.ts).
Answer:
import { Component } from '@angular/core';
@Component({
standalone: true,
selector: 'app-hello-standalone',
template: `<h1>Hello, Standalone!</h1>`,
imports: []
})
export class HelloStandaloneComponent {}
// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { HelloStandaloneComponent } from './hello-standalone.component';
bootstrapApplication(HelloStandaloneComponent);
Explanation:
This component sets standalone: true
and is bootstrapped directly using
bootstrapApplication
, bypassing the need for a root NgModule.
Implement Lazy Loading for a Feature Module
Question:
Set up a route '/admin'
that lazy loads the AdminModule
.
// Use loadChildren in the AppRoutingModule for 'admin' path.
// AdminModule is assumed to be a separate feature module.
Answer:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{ path: 'admin', loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule) },
{ path: '', redirectTo: 'admin', pathMatch: 'full' }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
Explanation:
This configuration postpones loading AdminModule
until the user navigates to /admin
, reducing the initial bundle size.
Implement a Custom Preloading Strategy
Question:
Write a custom preloading strategy SelectivePreloadingStrategy
that only preloads routes with data: { preload: true }
.
// Implement a PreloadingStrategy that checks route.data.preload before preloading.
Answer:
import { PreloadingStrategy, Route } from '@angular/router';
import { Observable, of } from 'rxjs';
export class SelectivePreloadingStrategy implements PreloadingStrategy {
preload(route: Route, load: Function): Observable<any> {
return route.data && route.data.preload ? load() : of(null);
}
}
Explanation:
This strategy selectively preloads modules that explicitly request it in their route configuration, optimizing performance.
Set Up Server-Side Rendering (SSR)
Question:
Configure an Angular Universal server module AppServerModule
that bootstraps
AppModule
for SSR.
// Implement a server module that imports AppModule and the ServerModule.
Answer:
import { NgModule } from '@angular/core';
import { ServerModule } from '@angular/platform-server';
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
@NgModule({
imports: [
AppModule,
ServerModule
],
bootstrap: [AppComponent]
})
export class AppServerModule {}
Explanation:
AppServerModule
configures Angular for server-side rendering by including
ServerModule
and bootstrapping the root component.
Use Renderer2 to Add an Element Dynamically
Question:
Create a directive appCreateSpan
that inserts a <span>
element with text “Hello” after the host element using Renderer2
in ngOnInit()
.
// Add a new span element as a sibling of the host element using Renderer2.
Answer:
import { Directive, OnInit, ElementRef, Renderer2 } from '@angular/core';
@Directive({
selector: '[appCreateSpan]'
})
export class CreateSpanDirective implements OnInit {
constructor(private el: ElementRef, private renderer: Renderer2) {}
ngOnInit() {
const span = this.renderer.createElement('span');
const text = this.renderer.createText('Hello');
this.renderer.appendChild(span, text);
this.renderer.appendChild(this.el.nativeElement.parentNode, span);
}
}
Explanation:
This directive demonstrates low-level DOM manipulation using Angular’s Renderer2
for platform-safe operations.
Create a Dynamic Form from a Configuration
Question:
Generate a reactive form dynamically from a config array, where each element specifies a form control name and initial value. Render the controls in a template.
// Given a config: [{name: 'firstName', value: ''}, {name: 'age', value: 30}],
// create a FormGroup dynamically and display the controls in a template.
Answer:
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
@Component({
selector: 'app-dynamic-form',
template: `
<form [formGroup]="form">
<div *ngFor="let field of config">
<label>{{field.name}}</label>
<input [formControlName]="field.name">
</div>
</form>
`
})
export class DynamicFormComponent implements OnInit {
config = [
{ name: 'firstName', value: '' },
{ name: 'age', value: 30 }
];
form!: FormGroup;
ngOnInit() {
const group: {[key: string]: FormControl} = {};
this.config.forEach(item => {
group[item.name] = new FormControl(item.value);
});
this.form = new FormGroup(group);
}
}
Explanation:
The component creates a FormGroup
from a configuration object, dynamically building controls. The template uses formGroup
and formControlName
to display fields.
Create a Custom Structural Directive That Conditionally Inserts a Template
Question:
Write a directive appIfOdd
that only renders its host template if the given number is odd.
// *appIfOdd="someNumber" should display the element only if someNumber is odd.
Answer:
import { Directive, Input, ViewContainerRef, TemplateRef } from '@angular/core';
@Directive({
selector: '[appIfOdd]'
})
export class IfOddDirective {
@Input() set appIfOdd(value: number) {
this.viewContainer.clear();
if (value % 2 !== 0) {
this.viewContainer.createEmbeddedView(this.templateRef);
}
}
constructor(private viewContainer: ViewContainerRef, private templateRef: TemplateRef<any>) {}
}
Explanation:
This structural directive checks the value. If it’s odd, it creates a view; otherwise, it clears it, controlling DOM rendering based on logic.
Create a Complex Animation in Angular
Question:
Add an animation to AnimatedBoxComponent
that transitions the box’s background color from red to blue over 1 second when toggled.
// Implement an Angular animation triggered by a component boolean state.
// Animate a div background from red to blue.
Answer:
import { Component } from '@angular/core';
import { trigger, state, style, transition, animate } from '@angular/animations';
@Component({
selector: 'app-animated-box',
template: `
<div [@colorChange]="isBlue ? 'blue' : 'red'"></div>
<button (click)="toggle()">Toggle</button>
`,
styles: [`
div { width: 100px; height: 100px; }
`],
animations: [
trigger('colorChange', [
state('red', style({ backgroundColor: 'red' })),
state('blue', style({ backgroundColor: 'blue' })),
transition('red <=> blue', [animate('1s')])
])
]
})
export class AnimatedBoxComponent {
isBlue = false;
toggle() {
this.isBlue = !this.isBlue;
}
}
Explanation:
This uses Angular’s animation DSL to define states and transitions. The toggle()
method switches states, triggering a smooth color transition.
Write a Global Error Handler
Question:
Implement a global error handler AppErrorHandler
that logs errors to the console. Register it as the ErrorHandler
provider.
// Implement a class that extends ErrorHandler and logs errors.
Answer:
import { ErrorHandler, Injectable, NgModule } from '@angular/core';
@Injectable()
export class AppErrorHandler implements ErrorHandler {
handleError(error: any): void {
console.error('Global Error:', error);
}
}
@NgModule({
providers: [{ provide: ErrorHandler, useClass: AppErrorHandler }]
})
export class CoreModule {}
Explanation:
By providing a custom ErrorHandler
, all uncaught errors are centrally managed, enabling consistent logging and handling strategies.
Bootstrap an Application Using bootstrapApplication with Dependencies
Question:
Use bootstrapApplication
from Angular 14+ to bootstrap AppComponent
without a traditional AppModule
. Ensure that:
- You include a basic service (
AppService
) and provide it in the root injector. - You configure the application with a simple provider (
provideHttpClient()
from@angular/common/http
). - You handle potential errors in the bootstrapping process.
// In main.ts, bootstrap the AppComponent using bootstrapApplication.
// Ensure that a service is injected and HTTP client support is provided.
Answer:
import { bootstrapApplication } from '@angular/platform-browser';
import { provideHttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { AppComponent } from './app.component';
// Define a simple service
@Injectable({ providedIn: 'root' })
export class AppService {
getData() {
return 'Hello from AppService!';
}
}
// Bootstrap the application with additional providers
bootstrapApplication(AppComponent, {
providers: [provideHttpClient(), AppService],
}).catch(err => console.error('Bootstrap error:', err));
Explanation:
bootstrapApplication
removes the need for anAppModule
, making the application more modular.- The
AppService
is registered as a provider and can be injected into components. provideHttpClient()
ensures that HTTP-related services can be used within the application.- Error handling is added to catch potential bootstrapping failures.
Create a Custom UrlSerializer
Question:
Implement a custom UrlSerializer
that converts all route paths to uppercase.
// Implement a CustomUrlSerializer extending DefaultUrlSerializer and transform paths to uppercase.
Answer:
import { UrlSerializer, UrlTree, DefaultUrlSerializer } from '@angular/router';
import { Injectable } from '@angular/core';
@Injectable()
export class UpperCaseUrlSerializer implements UrlSerializer {
private defaultSerializer = new DefaultUrlSerializer();
parse(url: string): UrlTree {
return this.defaultSerializer.parse(url.toLowerCase());
}
serialize(tree: UrlTree): string {
const url = this.defaultSerializer.serialize(tree);
return url.toUpperCase();
}
}
Explanation:
This custom serializer ensures all serialized URLs are uppercase. By delegating parsing to the default serializer, it maintains compatibility with Angular’s routing mechanics.
Create a Reusable Modal Dialog Using Angular CDK
Question:
Use Angular CDK’s Portal to create a reusable ModalContainerComponent
that inserts a component dynamically as content.
// Implement a container that uses PortalHost to display dynamic content.
Answer:
import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { CdkPortalOutlet, ComponentPortal } from '@angular/cdk/portal';
@Component({
selector: 'app-modal-container',
template: `
<div class="modal-overlay">
<div class="modal-content">
<ng-template cdkPortalOutlet></ng-template>
</div>
</div>
`
})
export class ModalContainerComponent implements OnInit {
@ViewChild(CdkPortalOutlet, {static: true}) portalOutlet!: CdkPortalOutlet;
@Input() content!: any;
ngOnInit() {
if (this.content) {
this.portalOutlet.attach(new ComponentPortal(this.content));
}
}
}
Explanation:
The component uses a CdkPortalOutlet
to dynamically insert a ComponentPortal
. This enables flexible, on-the-fly modal content rendering.
Create a Custom RxJS Operator in a Service
Question:
Write a service TransformService
that provides a custom RxJS operator
doubleValues()
which doubles numerical values from an observable stream.
// Implement a method that returns a function applying map(x => x * 2).
Answer:
import { Injectable } from '@angular/core';
import { map, Observable } from 'rxjs';
@Injectable({providedIn: 'root'})
export class TransformService {
doubleValues() {
return (source: Observable<number>) => source.pipe(map(value => value * 2));
}
}
Explanation:
This service exposes a custom operator function. Developers can use it with
source$.pipe(transformService.doubleValues())
to modify data streams.
Setup a Multi-Provider for a Shared Token
Question:
Create a multi-provider for a token LOGGER
that collects multiple logger implementations into an array.
// Provide LOGGER multiple times and inject them as an array.
Answer:
import { InjectionToken, Provider } from '@angular/core';
export interface Logger {
log(msg: string): void;
}
export const LOGGER = new InjectionToken<Logger[]>('LOGGER');
class ConsoleLogger implements Logger {
log(msg: string) { console.log('ConsoleLogger:', msg); }
}
class FileLogger implements Logger {
log(msg: string) { console.log('FileLogger:', msg); }
}
export const loggerProviders: Provider[] = [
{ provide: LOGGER, useClass: ConsoleLogger, multi: true },
{ provide: LOGGER, useClass: FileLogger, multi: true }
];
Explanation:
Multiple providers with multi: true
aggregate into a single injection token array. Injecting LOGGER
returns both logger instances.
A Structural Directive to Repeat Content N Times Using appTimes
Question:
Implement another version of a repetition directive *appTimes="count"
that creates an array from 0 to count-1 and uses ngFor
internally.
// Implement a directive that uses an embedded view for each iteration index.
Answer:
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[appTimes]'
})
export class TimesDirective {
@Input() set appTimes(count: number) {
this.viewContainer.clear();
for (let i = 0; i < count; i++) {
this.viewContainer.createEmbeddedView(this.templateRef, { $implicit: i });
}
}
constructor(private viewContainer: ViewContainerRef, private templateRef: TemplateRef<any>) {}
}
Explanation:
Similar to a previous example, but now the context $implicit
passes the index. This is a common directive pattern replicating *ngFor
functionality.
OnPush Component With Manual Change Detection
Question:
Create a component ManualRefreshComponent
with
ChangeDetectionStrategy.OnPush
and a button that, when clicked, calls ChangeDetectorRef.markForCheck()
to update the view.
// OnPush component that changes a value and triggers change detection manually.
Answer:
import { Component, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
@Component({
selector: 'app-manual-refresh',
template: `
<div>{{counter}}</div>
<button (click)="increment()">Increment</button>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ManualRefreshComponent {
counter = 0;
constructor(private cdr: ChangeDetectorRef) {}
increment() {
this.counter++;
this.cdr.markForCheck();
}
}
Explanation:
With OnPush, changes to counter
won’t reflect unless triggered by input or manually marked for check. Calling markForCheck()
prompts Angular to update the view.
Implement a Dynamic Theme Switcher
Question:
Create a ThemeService
that uses a BehaviorSubject to track the current theme
('light'
or 'dark'
) and a component that uses it to apply a class on the body
element dynamically.
// Implement a service with a BehaviorSubject<string> and a component that subscribes and updates body class.
Answer:
// theme.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({providedIn: 'root'})
export class ThemeService {
currentTheme$ = new BehaviorSubject<string>('light');
setTheme(theme: string) {
this.currentTheme$.next(theme);
}
}
// theme-toggle.component.ts
import { Component, OnInit, Renderer2 } from '@angular/core';
import { ThemeService } from './theme.service';
@Component({
selector: 'app-theme-toggle',
template: `
<button (click)="toggle()">Toggle Theme</button>
`
})
export class ThemeToggleComponent implements OnInit {
constructor(private themeService: ThemeService, private renderer: Renderer2) {}
ngOnInit() {
this.themeService.currentTheme$.subscribe(theme => {
this.renderer.removeClass(document.body, 'light');
this.renderer.removeClass(document.body, 'dark');
this.renderer.addClass(document.body, theme);
});
}
toggle() {
const newTheme = this.themeService.currentTheme$.value === 'light' ? 'dark' : 'light';
this.themeService.setTheme(newTheme);
}
}
Explanation:
This service broadcasts theme changes. The component updates the body
class based on the current theme, enabling dynamic styling.
Create a Custom Pipe That Filters an Array of Objects
Question:
Write a pipe FilterByNamePipe
that filters an array of objects by a provided substring on the name
property.
// Implement a pipe: {{ list | filterByName:'searchTerm' }}
// Returns only items whose name includes the searchTerm.
Answer:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({name: 'filterByName', pure: true})
export class FilterByNamePipe implements PipeTransform {
transform(items: { name: string }[], searchTerm: string): { name: string }[] {
if (!searchTerm) return items;
return items.filter(item => item.name.toLowerCase().includes(searchTerm.toLowerCase()));
}
}
Explanation:
This pure pipe filters data based on a search term. It’s commonly used in lists or dropdowns to dynamically show relevant items.
Angular Developer hiring resources
Our clients
Popular Angular Development questions
How does Angular handle state management in complex applications?
In Angular, it was possible to manage state with the use of services with RxJS for reactive data flows or the integration of library solutions like NgRx and Akita. This makes it predictable and scalable to manage application state, which in turn means changes will be more traceable, problems easier to debug, and components will stay consistent.
How do Angular services differ from components?
Services in Angular are primarily used to encapsulate business logic and data management; thus, making them reusable across components. Compared to components, which deal with UI and interaction logic, services provide functionalities shared in many parts of an application, such as retrieval of data and state management.
What are the key differences between Angular and Angular.js?
Angular, also referred to as Angular 2+ or Angular 2, is a complete rewrite from AngularJS-often called Angular 1. Its new features are a component-based architecture, enhanced support for TypeScript, and increased performance due to AOT compilation. Unlike the popular belief that Angular.js is scope-based, it’s not a pure MV* architecture, and it lacks a lot of the modern features of Angular.
Which is best with .NET: React or Angular?
While Angular mostly targets enterprise-level applications because of its opinionated framework and in-built features that go in line with .NET architecture, React gives more space for customization and is perfect for small projects with lightweight front-ends. It depends on what decisions are being made, given the size of the project and the kind of customization.
What are the 5 advantages of Angular?
The major advantages Angular provides are the following: robust architecture to build complex apps with modular components; two-way data binding for efficient real-time linking between the model and view; useful support of dependency injection for effective code; and extended support in tooling and testing. It is supported by a strong backing of the robust community, with continuous updates from Google.
What is Angular used for? – Lemon.io
Angular is the Front-end framework for building dynamic single-page web applications. Developed by Google, it provides a strong structure for complex applications, hence sustaining two-way data binding, dependency injection, and modular components. It is broadly used in developing interactive and scalable web apps that work effectively in a browser window.
Interview Questions by role
Interview Questions by skill
Interview Questions
Interview Questions
Interview Questions
Interview Questions
Interview Questions
Interview Questions
Interview Questions
Interview Questions
Interview Questions
Interview Questions
Interview Questions
Interview Questions
Interview Questions
Interview Questions