Interview Questions Overview
Estimated Time : 4 hours | Difficulty : All Levels | Prerequisites : Complete Angular Course
This comprehensive guide covers Angular interview questions from basic to advanced levels, helping you prepare for technical interviews at any stage of your career.
Core Angular Concepts
Q1: What is Angular and how does it differ from AngularJS?
Angular is a TypeScript-based platform for building web applications, completely rewritten from AngularJS.Key differences:
Architecture : Angular uses component-based architecture; AngularJS uses MVC
Language : Angular uses TypeScript; AngularJS uses JavaScript
Mobile : Angular supports mobile development; AngularJS doesn’t
Performance : Angular has improved change detection and AOT compilation
CLI : Angular has powerful CLI; AngularJS doesn’t have official CLI
Q2: Explain the Angular component lifecycle hooks
@ Component ({ ... })
export class ExampleComponent implements OnInit , OnChanges , OnDestroy {
// Called once before ngOnInit when inputs change
ngOnChanges ( changes : SimpleChanges ) {}
// Called once after first ngOnChanges
ngOnInit () {}
// Called every change detection cycle
ngDoCheck () {}
// Called once after first ngDoCheck
ngAfterContentInit () {}
// Called after every ngDoCheck
ngAfterContentChecked () {}
// Called once after first ngAfterContentChecked
ngAfterViewInit () {}
// Called after every ngAfterContentChecked
ngAfterViewChecked () {}
// Called before component is destroyed
ngOnDestroy () {}
}
Order : constructor → ngOnChanges → ngOnInit → ngDoCheck → ngAfterContentInit → ngAfterContentChecked → ngAfterViewInit → ngAfterViewChecked → ngOnDestroy
Q3: What are Angular Signals?
Signals are a reactive primitive introduced in Angular 16 for fine-grained reactivity. // Creating signals
const count = signal ( 0 );
const name = signal ( 'Angular' );
// Reading signals
console . log ( count ()); // 0
// Updating signals
count . set ( 5 );
count . update ( v => v + 1 );
// Computed signals (derived values)
const doubled = computed (() => count () * 2 );
// Effects (side effects)
effect (() => {
console . log ( `Count is: ${ count () } ` );
});
Benefits :
Fine-grained reactivity (only affected components update)
Better performance than Zone.js change detection
Simpler mental model than RxJS for state
Lazy evaluation of computed values
Q4: Explain the difference between OnPush and Default change detection
Default Strategy : Angular checks the entire component tree on every change detection cycle.OnPush Strategy : Component only checked when:
Input reference changes
Event from component or child
Async pipe emits new value
Manual markForCheck() or detectChanges()
@ Component ({
changeDetection: ChangeDetectionStrategy . OnPush
})
export class OptimizedComponent {
data = input < Data >(); // Only triggers on reference change
// Manual trigger if needed
private cdr = inject ( ChangeDetectorRef );
forceUpdate () {
this . cdr . markForCheck ();
}
}
Best Practice : Use OnPush everywhere with immutable data patterns.
Dependency Injection
Q5: What is Dependency Injection and how does Angular implement it?
DI is a design pattern where dependencies are provided to a class rather than created by it. // Service definition
@ Injectable ({ providedIn: 'root' })
export class UserService {
getUser ( id : string ) : Observable < User > { ... }
}
// Injection in component
@ Component ({ ... })
export class UserComponent {
private userService = inject ( UserService );
// Or via constructor
constructor ( private userService : UserService ) {}
}
Hierarchical Injector :
Root level: providedIn: 'root' (singleton)
Module level: providers array in NgModule
Component level: providers in component (new instance per component)
Benefits : Loose coupling, testability, reusability
Q6: Explain providedIn options
// Singleton for entire app
@ Injectable ({ providedIn: 'root' })
// Singleton per lazy-loaded module
@ Injectable ({ providedIn: 'any' })
// Provided to specific module
@ Injectable ({ providedIn: SomeModule })
// Platform level (shared across apps)
@ Injectable ({ providedIn: 'platform' })
// Not automatically provided (must add to providers array)
@ Injectable ()
Templates & Data Binding
Q7: Explain the different types of data binding in Angular
<!-- 1. Interpolation (one-way) -->
< p > {{ message }} </ p >
<!-- 2. Property Binding (one-way) -->
< img [src] = "imageUrl" >
< button [disabled] = "isDisabled" >
<!-- 3. Event Binding (one-way) -->
< button (click) = "onClick()" >
<!-- 4. Two-way Binding -->
< input [(ngModel)] = "name" >
<!-- Equivalent to: -->
< input [ngModel] = "name" (ngModelChange) = "name = $event" >
<!-- 5. Attribute Binding -->
< td [attr.colspan] = "colSpan" >
<!-- 6. Class Binding -->
< div [class.active] = "isActive" >
<!-- 7. Style Binding -->
< div [style.color] = "textColor" >
Q8: What is the difference between ng-template, ng-container, and ng-content?
ng-template : Defines a template that is not rendered by default< ng-template #myTemplate let-name = "name" >
< p > Hello, {{ name }} </ p >
</ ng-template >
< ng-container *ngTemplateOutlet = "myTemplate; context: {name: 'Angular'}" />
ng-container : Logical grouping without adding extra DOM elements< ng-container *ngIf = "condition" >
< p > No wrapper element added </ p >
</ ng-container >
ng-content : Content projection (transclusion)<!-- parent-component.html -->
< child-component >
< p > This content is projected </ p >
</ child-component >
<!-- child-component.html -->
< div class = "wrapper" >
< ng-content ></ ng-content >
</ div >
Routing
Q9: What are Route Guards and their types?
// CanActivate - Control access to route
export const authGuard : CanActivateFn = ( route , state ) => {
const auth = inject ( AuthService );
return auth . isLoggedIn () ? true : inject ( Router ). createUrlTree ([ '/login' ]);
};
// CanActivateChild - Control access to child routes
export const adminGuard : CanActivateChildFn = () => {
return inject ( AuthService ). isAdmin ();
};
// CanDeactivate - Prevent leaving route
export const unsavedChangesGuard : CanDeactivateFn < HasUnsavedChanges > = ( component ) => {
return component . hasUnsavedChanges ()
? confirm ( 'Discard changes?' )
: true ;
};
// CanMatch - Prevent route matching entirely
export const featureGuard : CanMatchFn = () => {
return inject ( FeatureService ). isEnabled ( 'new-feature' );
};
// Resolve - Pre-fetch data before activation
export const userResolver : ResolveFn < User > = ( route ) => {
return inject ( UserService ). getUser ( route . params [ 'id' ]);
};
// Routes configuration
const routes : Routes = [
{
path: 'admin' ,
canActivate: [ authGuard ],
canActivateChild: [ adminGuard ],
canMatch: [ featureGuard ],
children: [ ... ]
}
];
Q10: Explain lazy loading in Angular
// Lazy load routes
const routes : Routes = [
{
path: 'products' ,
loadChildren : () => import ( './products/products.routes' )
. then ( m => m . PRODUCTS_ROUTES )
},
{
path: 'admin' ,
loadComponent : () => import ( './admin/admin.component' )
. then ( m => m . AdminComponent )
}
];
// Preloading strategies
@ NgModule ({
imports: [
RouterModule . forRoot ( routes , {
preloadingStrategy: PreloadAllModules
// Or custom: CustomPreloadingStrategy
})
]
})
// Deferrable views (Angular 17+)
@ defer ( on viewport ) {
< app - heavy - component />
} @ placeholder {
< app - skeleton />
}
RxJS & HTTP
Q11: What is the difference between Subject, BehaviorSubject, and ReplaySubject?
// Subject - No initial value, late subscribers miss past emissions
const subject = new Subject < number >();
subject . next ( 1 );
subject . subscribe ( v => console . log ( v )); // Won't receive 1
subject . next ( 2 ); // Logs: 2
// BehaviorSubject - Has initial/current value
const behavior = new BehaviorSubject < number >( 0 );
behavior . next ( 1 );
behavior . subscribe ( v => console . log ( v )); // Logs: 1 (current value)
behavior . getValue (); // Returns current value: 1
// ReplaySubject - Replays N values to new subscribers
const replay = new ReplaySubject < number >( 2 ); // Buffer size 2
replay . next ( 1 );
replay . next ( 2 );
replay . next ( 3 );
replay . subscribe ( v => console . log ( v )); // Logs: 2, 3
// AsyncSubject - Only emits last value on completion
const async = new AsyncSubject < number >();
async . next ( 1 );
async . next ( 2 );
async . subscribe ( v => console . log ( v )); // Nothing yet
async . complete (); // Now logs: 2
Q12: Explain common RxJS operators
// Transformation
map ( x => x * 2 ) // Transform each value
switchMap ( x => http . get ( x )) // Switch to new observable, cancel previous
mergeMap ( x => http . get ( x )) // Merge all inner observables
concatMap ( x => http . get ( x )) // Queue inner observables sequentially
// Filtering
filter ( x => x > 0 ) // Filter values
distinctUntilChanged () // Only emit when value changes
debounceTime ( 300 ) // Wait for pause in emissions
throttleTime ( 300 ) // Rate limit emissions
take ( 5 ) // Take first N values
takeUntil ( destroy$ ) // Take until signal
// Combination
combineLatest ([ a$ , b$ ]) // Emit when any source emits
forkJoin ([ a$ , b$ ]) // Emit when all complete
merge ( a$ , b$ ) // Merge multiple streams
withLatestFrom ( other$ ) // Combine with latest from another
// Error Handling
catchError ( err => of ( null )) // Handle errors
retry ( 3 ) // Retry on error
retryWhen ( ... ) // Custom retry logic
// Utility
tap ( x => console . log ( x )) // Side effects
shareReplay ( 1 ) // Share and cache last value
Testing
Q13: How do you test Angular components?
describe ( 'UserComponent' , () => {
let component : UserComponent ;
let fixture : ComponentFixture < UserComponent >;
let userServiceSpy : jasmine . SpyObj < UserService >;
beforeEach ( async () => {
userServiceSpy = jasmine . createSpyObj ( 'UserService' , [ 'getUser' ]);
userServiceSpy . getUser . and . returnValue ( of ({ id: '1' , name: 'Test' }));
await TestBed . configureTestingModule ({
imports: [ UserComponent ],
providers: [
{ provide: UserService , useValue: userServiceSpy }
]
}). compileComponents ();
fixture = TestBed . createComponent ( UserComponent );
component = fixture . componentInstance ;
});
it ( 'should create' , () => {
expect ( component ). toBeTruthy ();
});
it ( 'should display user name' , () => {
fixture . componentRef . setInput ( 'userId' , '1' );
fixture . detectChanges ();
const nameElement = fixture . nativeElement . querySelector ( '.user-name' );
expect ( nameElement . textContent ). toContain ( 'Test' );
});
it ( 'should call service on init' , () => {
fixture . componentRef . setInput ( 'userId' , '1' );
fixture . detectChanges ();
expect ( userServiceSpy . getUser ). toHaveBeenCalledWith ( '1' );
});
});
Build-time optimizations:
AOT compilation
Tree shaking
Code splitting / lazy loading
Bundle analysis and optimization
Runtime optimizations: // 1. OnPush change detection
@ Component ({
changeDetection: ChangeDetectionStrategy . OnPush
})
// 2. TrackBy for lists
@ for ( item of items (); track item . id ) {
< app - item [ item ] = "item" />
}
// 3. Signals for reactive state
count = signal ( 0 );
doubled = computed (() => this . count () * 2 );
// 4. Deferrable views
@ defer ( on viewport ) {
< heavy - component />
}
// 5. Virtual scrolling for large lists
< cdk - virtual - scroll - viewport itemSize = "50" >
< div * cdkVirtualFor = "let item of items" > {{ item }} </ div >
</ cdk - virtual - scroll - viewport >
// 6. Pure pipes
@ Pipe ({ pure: true })
// 7. Unsubscribe properly
takeUntilDestroyed ( this . destroyRef )
Advanced Topics
Q15: What is Zone.js and how does Angular use it?
Zone.js is a library that patches async APIs (setTimeout, Promise, XHR, etc.) to notify Angular when async operations complete. // Zone.js patches these to trigger change detection:
setTimeout (() => {})
Promise . resolve (). then (() => {})
fetch ( '/api' ). then (() => {})
element . addEventListener ( 'click' , () => {})
// Running outside Angular zone (for performance)
@ Component ({ ... })
export class PerformanceComponent {
private ngZone = inject ( NgZone );
heavyOperation () {
this . ngZone . runOutsideAngular (() => {
// This won't trigger change detection
setInterval (() => this . trackMouse (), 10 );
});
}
updateUI () {
this . ngZone . run (() => {
// This triggers change detection
this . value = 'updated' ;
});
}
}
Zoneless Angular (experimental in v18) :bootstrapApplication ( App , {
providers: [
provideExperimentalZonelessChangeDetection ()
]
});
Q16: Explain Angular SSR and Hydration
Server-Side Rendering (SSR) renders the application on the server, sending HTML to the client.Hydration reuses the server-rendered DOM instead of re-creating it.// app.config.server.ts
export const config : ApplicationConfig = {
providers: [
provideServerRendering (),
provideClientHydration ()
]
};
// Benefits:
// - Better SEO (search engines see content)
// - Faster First Contentful Paint
// - Better Core Web Vitals
// - Works with JavaScript disabled
// Hydration skipping for dynamic content
@ Component ({
host: { ngSkipHydration: 'true' }
})
export class DynamicComponent {}
Coding Challenges
Challenge 1: Implement a debounced search
@ Component ({
template: `
<input [formControl]="searchControl" placeholder="Search...">
@for (result of results(); track result.id) {
<div>{{ result.name }}</div>
}
`
})
export class SearchComponent {
private searchService = inject ( SearchService );
private destroyRef = inject ( DestroyRef );
searchControl = new FormControl ( '' );
results = signal < SearchResult []>([]);
ngOnInit () {
this . searchControl . valueChanges . pipe (
debounceTime ( 300 ),
distinctUntilChanged (),
filter ( term => term . length >= 2 ),
switchMap ( term => this . searchService . search ( term )),
takeUntilDestroyed ( this . destroyRef )
). subscribe ( results => this . results . set ( results ));
}
}
Challenge 2: Create a reusable modal service
@ Injectable ({ providedIn: 'root' })
export class ModalService {
private vcr !: ViewContainerRef ;
setContainer ( vcr : ViewContainerRef ) {
this . vcr = vcr ;
}
open < T , R >( component : Type < T >, data ?: Partial < T >) : Observable < R > {
const subject = new Subject < R >();
const ref = this . vcr . createComponent ( component );
if ( data ) {
Object . entries ( data ). forEach (([ key , value ]) => {
ref . setInput ( key , value );
});
}
// Assuming component has 'closed' output
ref . instance . closed ?. subscribe (( result : R ) => {
subject . next ( result );
subject . complete ();
ref . destroy ();
});
return subject . asObservable ();
}
}
Tips for Success
Understand Fundamentals Know DI, change detection, and component lifecycle deeply
Practice Coding Be ready to write code on whiteboard or shared editor
Know RxJS Understand operators and when to use them
Discuss Trade-offs Show you can evaluate different approaches
Next: Best Practices Learn Angular best practices and style guide