Angular interview questions and answers for 2025

hero image

Angular Interview Questions for Freshers and Intermediate Levels

1.

What is Angular, and how is it different from AngularJS?

Answer

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.
2.

What are Angular Components?

Answer

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 { }
3.

Explain the role of Modules in Angular.

Answer

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 {}
4.

What are Directives in Angular?

Answer

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).
5.

Explain Data Binding in Angular.

Answer

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">
6.

What are Pipes in Angular?

Answer

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>
7.

Describe Angular’s Lifecycle Hooks.

Answer

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.
8.

What is Dependency Injection (DI) in Angular?

Answer

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.

9.

How do you create and use Services in Angular?

Answer

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();
}
10.

What is the difference between Template-Driven and Reactive Forms?

Answer
  • 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 and FormGroup classes. More flexible, scalable, and testable. Form logic resides in TypeScript code.
11.

How do you implement Routing in Angular?

Answer

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 {}
12.

Explain the concept of Guards in Angular Routing.

Answer

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.

13.

What is the Async Pipe in Angular?

Answer

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>
14.

How do you communicate between parent and child components?

Answer

Use @Input() and @Output() decorators.

  • @Input() passes data from parent to child.
  • @Output() along with EventEmitter sends events/data from child to parent.
15.

What is Change Detection in Angular?

Answer

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.

16.

How do you create a Custom Pipe?

Answer

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('');
  }
}
17.

Explain the concept of ViewEncapsulation in Angular.

Answer

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.
18.

What is the Angular CLI?

Answer

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
19.

What is AOT Compilation in Angular?

Answer

Ahead-Of-Time (AOT) compilation pre-compiles templates and components at build time, resulting in smaller bundles and faster runtime performance.

20.

How do you handle HTTP requests in Angular?

Answer

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));
21.

What is NgZone in Angular?

Answer

NgZone helps Angular detect changes outside the normal Angular zone (like DOM events or timers) and trigger change detection manually if needed.

22.

Explain Structural vs. Attribute Directives with examples.

Answer
  • Structural Directive: Changes the DOM structure. E.g., ngIf, ngFor.
  • Attribute Directive: Changes the appearance or behavior of an element. E.g.,
    ngClass, ngStyle.
23.

What are Angular Animations?

Answer

Angular Animations use the Angular animations library (@angular/animations) to animate transitions between states in components. They offer a declarative API for sophisticated animations.

24.

How do you optimize Angular applications for performance?

Answer
  • Use AOT compilation.
  • Lazy load modules.
  • OnPush change detection.
  • TrackBy in *ngFor.
  • Minimize watchers and unnecessary computations.
  • Use memoized selectors (e.g., with NgRx).
25.

What is the purpose of ng-content?

Answer

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>
26.

What is a FormControl in Angular?

Answer

FormControl represents a single form input field’s value and validation status. It’s the basic building block of Reactive Forms.

27.

What is the role of @ViewChild() and @ViewChildren()?

Answer

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.
28.

How to share data between unrelated components?

Answer

Use a shared service with a BehaviorSubject or Subject to hold data. Both components inject the service and subscribe to changes.

29.

How do you define a custom event from a child component?

Answer

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!');
}
30.

What is the difference between ngOnInit() and the constructor of a component?

Answer
  • 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

1.

Can you explain the Angular Change Detection Strategy and the OnPush strategy in detail?

Answer

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.

2.

How do you implement Lazy Loading in Angular for performance optimization?

Answer

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.

3.

What are Angular Elements, and how might you use them?

Answer

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.

4.

Explain the concept of NgModules vs. Standalone Components in Angular (Angular 14+).

Answer

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.

5.

How would you handle state management in an Angular application?

Answer

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.
6.

Explain the difference between ViewContainerRef and TemplateRef.

Answer
  • 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.
7.

How do you create a Custom Structural Directive?

Answer

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();
    }
  }
}
8.

How do you optimize Angular applications using trackBy with *ngFor?

Answer

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;
}
9.

How do you handle complex forms and custom validators in Reactive Forms?

Answer

Create custom validators as functions that return an error object if invalid, or null if valid. Use Validators.compose() or directly attach validators to FormControls and
FormGroups.

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])
});
10.

Can you explain Angular’s Renderer2 and its use cases?

Answer

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.

11.

How do you implement custom error handling with the Angular HttpInterceptor?

Answer

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);
      })
    );
  }
}
12.

What are the benefits of using Angular Universal (Server-Side Rendering)?

Answer
  • 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.
13.

Explain the difference between forRoot() and forChild() methods in Angular Routing.

Answer
  • 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.
14.

What are Zone.js and its importance in Angular?

Answer

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.

15.

How would you create a dynamic component at runtime?

Answer

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;
16.

Discuss the pros and cons of OnPush change detection.

Answer

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.
17.

Explain the concept of Schematics in Angular CLI.

Answer

Schematics are templates for scaffolding code (components, modules, services). They enable custom code generation, automating repetitive tasks and maintaining consistency.

18.

How do you integrate Angular with a backend API and handle authentication tokens?

Answer

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.

19.

What are TemplateRef and EmbeddedViewRef?

Answer
  • TemplateRef: Represents an Angular template that can be instantiated.
  • EmbeddedViewRef: Represents an instantiated view of a template. Manipulating these references allows dynamic content rendering.
20.

How do you preload lazy-loaded modules?

Answer

Use the PreloadingStrategy interface and configure in the router. Preloading can fetch modules in the background after initial load, improving subsequent navigation speed.

21.

Discuss the importance of content projection and how to implement it.

Answer

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>
22.

What is a multi-provider in Angular DI?

Answer

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 }
]
23.

How do you approach testing Angular components with dependencies?

Answer

Use TestBed to configure a testing module, mock dependencies (services) with Jasmine spies or HttpTestingModule, and then test component logic and DOM rendering.

24.

Explain differential loading in Angular.

Answer

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.

25.

What are Ivy and its benefits in Angular?

Answer

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.
26.

How would you implement a custom preloading strategy?

Answer

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);
  }
}
27.

How do you debug Angular change detection cycles?

Answer
  • 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.
28.

What are RxJS Subjects, and how are they used in Angular?

Answer

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.
29.

How do you handle memory leaks in Angular?

Answer
  • 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.
30.

What is the difference between TestBed.createComponent() and fixture.detectChanges() in Angular testing?

Answer
  • 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

1.

Create a Custom Structural Directive

Answer

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.

2.

Implement a Custom Pipe with Side Effects

Answer

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.

3.

Write a Custom Async Validator

Answer

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.

4.

Create a Component that Dynamically Loads Another Component

Answer

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.

5.

Create a Route Guard That Depends on a Service

Answer

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.

6.

Create a Directive That Conditionally Applies a CSS Class

Answer

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.

7.

Implement OnPush Change Detection with trackBy

Answer

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.

8.

Create a Component Using the Async Pipe

Answer

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.

9.

Write Unit Tests for a Service

Answer

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.

10.

Create a Custom HttpInterceptor

Answer

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.

11.

Implement an NgRx Store for a Simple Counter

Answer

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.

12.

Implement Content Projection in a Component

Answer

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.

13.

Create a Standalone Component (Angular 14+)

Answer

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.

14.

Implement Lazy Loading for a Feature Module

Answer

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.

15.

Implement a Custom Preloading Strategy

Answer

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.

16.

Set Up Server-Side Rendering (SSR)

Answer

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.

17.

Use Renderer2 to Add an Element Dynamically

Answer

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.

18.

Create a Dynamic Form from a Configuration

Answer

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.

19.

Create a Custom Structural Directive That Conditionally Inserts a Template

Answer

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.

20.

Create a Complex Animation in Angular

Answer

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.

21.

Write a Global Error Handler

Answer

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.

22.

Bootstrap an Application Using bootstrapApplication with Dependencies

Answer

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 an AppModule, 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.
23.

Create a Custom UrlSerializer

Answer

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.

24.

Create a Reusable Modal Dialog Using Angular CDK

Answer

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.

25.

Create a Custom RxJS Operator in a Service

Answer

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.

26.

Setup a Multi-Provider for a Shared Token

Answer

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.

27.

A Structural Directive to Repeat Content N Times Using appTimes

Answer

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.

28.

OnPush Component With Manual Change Detection

Answer

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.

29.

Implement a Dynamic Theme Switcher

Answer

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.

30.

Create a Custom Pipe That Filters an Array of Objects

Answer

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
Hire Angular Developers
Hire fast and on budget—place a request, interview 1-3 curated developers, and get the best one onboarded by next Friday. Full-time or part-time, with optimal overlap.
Hire now
Q&A about hiring Angular Developers
Want to know more about hiring Angular Developers? Lemon.io got you covered
Read Q&A
Angular Developer Job Description Template
Attract top Angular developers with a clear, compelling job description. Use our expert template to save time and get high-quality applicants fast.
Check the Job Description

Hire remote Angular developers

Developers who got their wings at:
Testimonials
star star star star star
Gotta drop in here for some Kudos. I’m 2 weeks into working with a super legit dev on a critical project, and he’s meeting every expectation so far 👏
avatar
Francis Harrington
Founder at ProCloud Consulting, US
star star star star star
I recommend Lemon to anyone looking for top-quality engineering talent. We previously worked with TopTal and many others, but Lemon gives us consistently incredible candidates.
avatar
Allie Fleder
Co-Founder & COO at SimplyWise, US
star star star star star
I've worked with some incredible devs in my career, but the experience I am having with my dev through Lemon.io is so 🔥. I feel invincible as a founder. So thankful to you and the team!
avatar
Michele Serro
Founder of Doorsteps.co.uk, UK

Simplify your hiring process with remote Angular developers

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.

image

Ready-to-interview vetted Angular developers are waiting for your request