🅰️

Top 54 Angular Interview Questions & Answers (2026)

54 Questions 28 Beginner 17 Intermediate 9 Advanced

About Angular

Top 100 Angular interview questions covering components, modules, directives, services, dependency injection, RxJS, routing, forms, HTTP client, and performance optimization. Companies hiring for Angular roles test this knowledge at every stage — from first-round screens that check the basics to final-round conversations about real-world trade-offs.

What to Expect in a Angular Interview

Expect a mix of conceptual and practical Angular questions: clear definitions and core-concept checks for junior roles, hands-on scenario questions for mid-level roles, and architecture or trade-off discussions for senior roles. Interviewers usually move from foundational topics toward the kind of problems you'd actually face on the job.

How to Use This Guide

Work through the Angular questions in order — Beginner, then Intermediate, then Advanced — so each concept builds on the last. Every question has its own page; bookmark the ones that trip you up and revisit them the day before your interview.

Curated by Tech Baithak Editorial Team  ·  Last updated: May 2026

Beginner 28 questions

Core concepts every Angular developer must know.

01

What is Angular?

Angular is a TypeScript-based open-source web application framework developed and maintained by Google. It is a complete rewrite of AngularJS (Angular 1.x), released as Angular 2 in 2016. Angular is a full-featured, opinionated platform for building dynamic single-page applications (SPAs). Key characteristics: (1) TypeScript first: Angular is written in and strongly encourages TypeScript — static typing, decorators, and class-based OOP; (2) Component-based architecture: applications are built as a tree of reusable components; (3) Dependency Injection (DI): powerful built-in DI system for managing services and dependencies; (4) Two-way data binding: synchronizes the model (TypeScript) and view (HTML template) automatically; (5) RxJS/Reactive: uses Reactive Extensions for JavaScript (RxJS) for asynchronous programming with Observables; (6) Angular CLI: powerful command-line tool for scaffolding, building, testing, and deploying; (7) Module system (NgModules): organizes application into cohesive blocks of functionality. Angular is an enterprise-favorite — used by Google products, Microsoft, Samsung, and many large organizations. It has a steeper learning curve than React/Vue but provides more structure and conventions for large teams.

Open this question on its own page
02

What is the difference between AngularJS and Angular?

AngularJS (Angular 1.x) was the original framework released in 2010. Angular (2+) is a complete rewrite released in 2016. They are fundamentally different frameworks sharing only a name. Key differences: Language: AngularJS used JavaScript; Angular uses TypeScript (with JavaScript support). Architecture: AngularJS used MVC (Model-View-Controller) pattern with scopes and controllers; Angular uses component-based architecture — everything is a component. Data binding: AngularJS used two-way data binding everywhere (dirty checking with $scope — performance issues at scale); Angular uses one-way data flow by default with optional two-way binding ([(ngModel)]), and change detection is much more efficient. Dependency injection: AngularJS had a string-based DI system (fragile, especially with minification); Angular has a hierarchical DI system based on TypeScript type annotations. Performance: AngularJS used digest cycles for change detection (slow for large apps); Angular uses zones and smarter change detection (OnPush strategy available). Mobile support: AngularJS was not designed for mobile; Angular has NativeScript and Ionic support. CLI: AngularJS had no official CLI; Angular has the powerful Angular CLI. Modularity: AngularJS had limited module system; Angular has NgModules (and now standalone components). AngularJS reached end-of-life in December 2021.

Open this question on its own page
03

What is a component in Angular?

A component is the basic building block of an Angular application — it controls a patch of the screen (the view). Every component consists of three parts: (1) TypeScript class: contains the component's logic, properties (data), and methods. Decorated with @Component(); (2) HTML template: defines the component's view. Can be inline or external file. Uses Angular template syntax (data binding, directives); (3) CSS styles: optional scoped styles that apply only to this component's view. The @Component decorator provides metadata: @Component({ selector: "app-user-card", templateUrl: "./user-card.component.html", styleUrls: ["./user-card.component.css"] }). selector: CSS selector that identifies this component in parent templates (<app-user-card>). Data flow: @Input() properties receive data from parent; @Output() EventEmitter properties send events to parent. Generating a component via CLI: ng generate component user-card (or ng g c user-card) — creates 4 files: .ts, .html, .css, .spec.ts, and updates the module. Standalone components (Angular 14+): components that don't belong to any NgModule, declared with standalone: true in @Component — can directly import what they need.

Open this question on its own page
04

What is a module (NgModule) in Angular?

An NgModule is a class decorated with @NgModule() that organizes a cohesive block of functionality in an Angular application. It groups related components, directives, pipes, and services. The @NgModule decorator takes a metadata object: @NgModule({ declarations: [AppComponent, UserCardComponent, HighlightDirective], imports: [BrowserModule, FormsModule, HttpClientModule, RouterModule], exports: [UserCardComponent, HighlightDirective], providers: [UserService, { provide: Logger, useClass: ConsoleLogger }], bootstrap: [AppComponent] }). declarations: components, directives, and pipes that belong to this module; imports: other modules whose exported components/directives/pipes are needed by templates in this module; exports: declarations and imported modules that should be accessible to other modules that import this module; providers: services available via DI — available application-wide when in root module; bootstrap: (root module only) the component Angular creates first. Root module (AppModule): every app has one root module; platformBrowserDynamic().bootstrapModule(AppModule). Feature modules: organize related features; Shared modules: reusable components/pipes; Core module: app-wide singletons (services, interceptors). Lazy loading: feature modules can be lazy-loaded by the Router (load only when the route is first navigated to). Standalone components (Angular 14+) reduce the need for NgModules.

Open this question on its own page
05

What is data binding in Angular?

Data binding is the mechanism that synchronizes data between the component class (TypeScript) and its view (HTML template). Angular supports four types: (1) Interpolation (one-way: component → view): {{ expression }} — renders a component property in the template: <h1>Hello {{ username }}</h1>. The expression is evaluated and its string result is displayed; (2) Property binding (one-way: component → view): [property]="expression" — binds a DOM property or Angular input to a component expression: <img [src]="imageUrl">, <button [disabled]="isLoading">, <app-card [title]="post.title">. Use for non-string values; (3) Event binding (one-way: view → component): (event)="handler($event)" — listens to DOM or custom events: <button (click)="handleSubmit()">, <input (keyup)="onKeyUp($event)">; (4) Two-way binding: [(ngModel)]="property" — combines property + event binding for form elements. The "banana in a box" syntax. Requires FormsModule. <input [(ngModel)]="username"> — when the user types, username updates; when username changes in code, the input updates. Two-way binding is shorthand for [ngModel]="username" (ngModelChange)="username = $event".

Open this question on its own page
06

What are directives in Angular?

Directives are classes that add behavior to DOM elements or transform DOM layout. Three types: (1) Component directives: components ARE directives with a template (the most common type); (2) Structural directives: change the DOM structure by adding, removing, or manipulating elements. Prefixed with * (syntactic sugar for <ng-template>). Built-in: *ngIf: <div *ngIf="isLoggedIn; else loginTemplate"> — conditionally includes/removes an element; *ngFor: <li *ngFor="let item of items; let i = index; trackBy: trackById"> — repeats an element for each item in a list; *ngSwitch: conditionally display one of several views. New syntax (Angular 17+): @if, @for, @switch control flow; (3) Attribute directives: change the appearance or behavior of an existing element. Built-in: ngClass: [ngClass]="{ active: isActive, error: hasError }"; ngStyle: [ngStyle]="{ color: textColor, fontSize: fontSize + 'px' }"; ngModel: two-way binding; Custom attribute directive example: create a directive that turns text yellow on hover. Creating a directive: ng generate directive highlight — class with @Directive({ selector: '[appHighlight]' }) decorator. Access the host element via ElementRef and Renderer2 (DOM manipulation).

Open this question on its own page
07

What is a service in Angular?

A service is a TypeScript class decorated with @Injectable() that encapsulates reusable, non-UI logic and can be shared across multiple components. Services follow the single responsibility principle — handle specific functionality: data fetching, business logic, state management, logging, authentication. Why use services? Without services, components would contain all logic and data — components would be large, untestable, and logic would be duplicated. Services allow logic to be defined once and injected into any component that needs it. Creating a service: ng generate service user creates: @Injectable({ providedIn: "root" }) export class UserService { private users = []; getUsers() { return this.http.get("/api/users"); } }. providedIn: "root" registers the service as a singleton at the application root level — one instance shared across the entire application. Injecting a service: Angular's DI system automatically provides the service via constructor injection: constructor(private userService: UserService) {}. The class name as type annotation is the injection token. Service scope: providedIn: "root" (app-wide singleton), provided in a specific module (scoped singleton), or provided directly in a component's providers array (new instance per component). HttpClient, Router, ActivatedRoute are all examples of Angular built-in services.

Open this question on its own page
08

What is dependency injection (DI) in Angular?

Dependency Injection (DI) is a design pattern where a class declares its dependencies (via constructor parameters) and an external system (the injector) provides them, rather than the class creating them itself. Angular has a sophisticated hierarchical DI system. Why DI? Without DI: class UserComponent { service = new UserService(new HttpClient()); } — tightly coupled, hard to test. With DI: constructor(private userService: UserService) {} — Angular creates and injects UserService automatically. How Angular DI works: (1) Service is registered in a provider (root injector, module, component); (2) When Angular creates a component, it reads constructor parameter types; (3) Angular's injector looks up the registered service for each type; (4) Creates the service (if not already created) and injects it. Injection tokens: by default, the class type is the token. Custom tokens: InjectionToken<string>("API_URL"). Provider types: useClass (default — instantiate this class), useExisting (alias for existing provider), useValue (provide a literal value), useFactory (provide via factory function — supports async and conditional creation). Hierarchical injectors: Component-level providers create new instances per component. Child components can override services. The hierarchy: root injector → module injector → component injector → child component.

Open this question on its own page
09

What is Angular CLI?

The Angular CLI (Command Line Interface) is an official command-line tool that streamlines development workflows: project creation, code generation, building, testing, and serving. Install: npm install -g @angular/cli. Key commands: ng new project-name — create a new Angular application with routing and CSS/SCSS choice; ng serve — start dev server with HMR (hot module replacement) on localhost:4200; ng build — compile for production (output to dist/); ng build --configuration production — production build with optimizations (tree shaking, minification, AOT); ng test — run unit tests via Karma; ng e2e — run end-to-end tests; ng lint — run ESLint. Code generation (ng generate / ng g): ng g component user-profile, ng g service auth, ng g module features/admin --routing, ng g directive highlight, ng g pipe currency-format, ng g guard auth, ng g interceptor logging, ng g class user.model, ng g interface user, ng g enum user-role. Schematics: CLI uses schematics for code generation — Angular Material adds its own schematics (ng add @angular/material configures the entire library). ng update — update Angular and dependencies. ng add — add libraries with configuration. ng deploy — deploy to platforms (Firebase, GitHub Pages, etc.).

Open this question on its own page
10

What is the Angular template syntax?

Angular's template syntax extends HTML with special syntax for data binding, directives, and template references. Key elements: Data binding: interpolation {{ expr }}, property binding [prop]="expr", event binding (event)="handler()", two-way [(ngModel)]="prop". Built-in directives: *ngIf, *ngFor, *ngSwitch/ngSwitchCase/ngSwitchDefault, [ngClass], [ngStyle], [ngModel]. Template reference variables: #varName — creates a reference to a DOM element or component/directive: <input #emailInput> <button (click)="log(emailInput.value)">. Pipe operator: {{ value | pipeName:arg1:arg2 }} — transforms displayed values: {{ date | date:"MM/dd/yyyy" }}, {{ price | currency:"USD" }}, {{ text | uppercase }}, {{ json | json }}. Safe navigation operator (?.): {{ user?.address?.city }} — prevents null reference errors. Non-null assertion (!): {{ user!.name }} — tells TypeScript the value is definitely not null. ng-template: <ng-template #loadingTpl>Loading...</ng-template> — defines a template that can be rendered by directives or programmatically. ng-container: <ng-container *ngIf="..."> — a grouping element that doesn't add a DOM element. New Angular 17+ control flow: @if (condition) { } @else { }, @for (item of items; track item.id) { }, @switch (value) { @case ("a") { } }.

Open this question on its own page
11

What is Angular routing?

Angular Router enables navigation between views (components) in a SPA without full page reloads — updating the URL to reflect the current view while keeping the app state. Setup: import RouterModule.forRoot(routes) in AppModule with a routes array: const routes: Routes = [ { path: "", component: HomeComponent }, { path: "users", component: UsersComponent }, { path: "users/:id", component: UserDetailComponent }, { path: "admin", loadChildren: () => import("./admin/admin.module").then(m => m.AdminModule) }, { path: "**", component: NotFoundComponent } ];. Router outlet: <router-outlet></router-outlet> in the template — placeholder where routed components are rendered. Navigation: in templates: <a routerLink="/users/123" routerLinkActive="active">; programmatically: this.router.navigate(["/users", userId]); with query params: this.router.navigate(["/search"], { queryParams: { q: "angular" } }). Route parameters: read via ActivatedRoute: this.route.params.subscribe(params => { this.userId = params["id"]; }) or snapshot: this.route.snapshot.params["id"]. Child routes: nested routes within a parent component. Lazy loading: loadChildren defers loading the feature module until the route is first visited — improves initial load time. Guards: CanActivate, CanDeactivate, CanLoad protect routes.

Open this question on its own page
12

What are pipes in Angular?

Pipes transform data for display in templates without modifying the underlying data. They use the | operator in template expressions. Built-in pipes: DatePipe: {{ today | date:"fullDate" }} → "Monday, January 15, 2024"; CurrencyPipe: {{ price | currency:"EUR":"symbol":"1.2-2" }}; DecimalPipe: {{ value | number:"1.1-3" }}; PercentPipe: {{ ratio | percent }}; UpperCasePipe / LowerCasePipe / TitleCasePipe; JsonPipe: {{ obj | json }} — pretty-prints for debugging; AsyncPipe: {{ observable$ | async }} — subscribes to Observable or Promise, returns latest value, auto-unsubscribes on component destroy; SlicePipe: {{ array | slice:0:5 }}; KeyValuePipe: iterate over object properties in *ngFor. Chaining pipes: {{ name | uppercase | slice:0:5 }}. Pipe parameters: {{ date | date:"shortDate":"UTC" }} — colon-separated args. Custom pipes: ng g pipe truncate — implement PipeTransform interface with transform(value: string, limit: number): string. Pure vs impure pipes: pure (default) — only called when input reference changes (efficient); impure — called on every change detection cycle (use sparingly — can be slow). AsyncPipe is impure. Standalone pipe: add standalone: true to the pipe decorator.

Open this question on its own page
13

What is Angular Forms — Template-driven vs Reactive?

Angular provides two approaches to building forms: Template-driven forms: form logic lives in the HTML template using directives. Angular creates the form model automatically from the template. Simpler, less code. Requires FormsModule. Uses: ngModel, ngForm, ngModelGroup. Example: <form (ngSubmit)="onSubmit(form)" #form="ngForm"> <input name="email" ngModel required email> <span *ngIf="form.controls.email?.invalid">Invalid</span> </form>. Pros: easy to understand, minimal code for simple forms. Cons: harder to test (template-based), less control over validation, not suitable for complex dynamic forms. Reactive forms: form model is defined explicitly in the TypeScript class using FormGroup, FormControl, FormBuilder, and FormArray. Template is bound to the model. Requires ReactiveFormsModule. Example: form = this.fb.group({ email: ["", [Validators.required, Validators.email]], password: ["", Validators.minLength(8)] }); // Template: <form [formGroup]="form" (ngSubmit)="onSubmit()"> <input formControlName="email"> <span *ngIf="form.get("email").hasError("email")">Invalid email</span></form>. Pros: explicit control, easier unit testing, dynamic forms support (FormArray), better validation management, reactive with value changes Observable. Cons: more verbose. When to use: template-driven for simple forms; reactive for complex, dynamic forms requiring unit testing.

Open this question on its own page
14

What is the HttpClient in Angular?

Angular's HttpClient (from @angular/common/http) is the recommended way to make HTTP requests. It returns Observables (not Promises) and provides type safety, interceptors, and automatic JSON parsing. Setup: import HttpClientModule in AppModule (or use provideHttpClient() in standalone apps). Inject: constructor(private http: HttpClient) {}. Methods: this.http.get<User[]>("/api/users"), this.http.post<User>("/api/users", userData), this.http.put<User>("/api/users/1", userData), this.http.patch<User>("/api/users/1", partial), this.http.delete<void>("/api/users/1"). Type parameters: generic type specifies the response body type — enables TypeScript intellisense on the result. Options: this.http.get("/api/users", { params: new HttpParams().set("page", "2"), headers: new HttpHeaders({ "Authorization": "Bearer " + token }), observe: "response" }). observe option: "body" (default — just data), "response" (full HttpResponse with status, headers), "events" (stream of all HTTP events including upload progress). responseType: "json" (default), "text", "blob", "arraybuffer". Error handling: pipe with catchError from RxJS. The returned Observable does NOT make the HTTP request until subscribed — "cold" Observable. HttpClientModule vs HttpClient standalone: in Angular 15+, use provideHttpClient(withInterceptorsFromDi()) in main.ts.

Open this question on its own page
15

What is Angular change detection?

Change detection is Angular's mechanism for keeping the view (DOM) in sync with the component's data model. When data changes, Angular's change detector updates the DOM to reflect those changes. Default strategy (CheckAlways): Angular runs change detection for every component in the tree when ANY async event occurs (user interaction, HTTP response, timer). It traverses the entire component tree from root to leaf, checking if any bindings have changed, and updating the DOM where needed. This is simple but can be slow for large apps. When change detection runs: Angular uses Zone.js to intercept async events — it patches browser APIs (setTimeout, addEventListener, Promise, XHR) and notifies Angular to run change detection after any async operation completes. OnPush strategy: @Component({ changeDetection: ChangeDetectionStrategy.OnPush }) — change detection runs only when: an @Input() reference changes, an event originates from this component or its children, an Observable/Promise used with async pipe emits, or changeDetectorRef.markForCheck() is called manually. This can dramatically improve performance for components that receive large data arrays by reference. Immutability: OnPush requires working with immutable data — always create new arrays/objects instead of mutating. this.items = [...this.items, newItem] (new array reference triggers OnPush) vs this.items.push(newItem) (mutates, doesn't trigger OnPush). Angular 17+ introduced Signals as a new, more efficient change detection primitive.

Open this question on its own page
16

What are lifecycle hooks in Angular?

Angular lifecycle hooks are methods that Angular calls at specific points in a component's or directive's lifecycle, allowing you to respond to creation, change, and destruction events. Implement the corresponding interface for each hook. Order of execution: (1) ngOnChanges(changes: SimpleChanges): called when an @Input() property changes. Receives a SimpleChanges object with previous and current values. Called before ngOnInit and whenever inputs change. Implement OnChanges; (2) ngOnInit(): called once after the first ngOnChanges. Best place for initialization logic (fetch data, subscribe to route params). The component's inputs are available. Implement OnInit; (3) ngDoCheck(): called every change detection cycle — even when Angular doesn't detect changes itself. For custom change detection. Implement DoCheck; (4) ngAfterContentInit(): called once after Angular projects external content into the component view (). Implement AfterContentInit; (5) ngAfterContentChecked(): called after every check of projected content. Implement AfterContentChecked; (6) ngAfterViewInit(): called once after Angular initializes the component's view (and child views). Access DOM elements via @ViewChild here. Implement AfterViewInit; (7) ngAfterViewChecked(): called after every check of the component's view. Implement AfterViewChecked; (8) ngOnDestroy(): called just before Angular destroys the component. Clean up: unsubscribe from Observables, clear timers, remove event listeners. Implement OnDestroy. Most commonly used: ngOnInit (initialization) and ngOnDestroy (cleanup).

Open this question on its own page
17

What is @Input() and @Output() in Angular?

@Input() and @Output() are decorators that define component communication: how data flows between parent and child components. @Input() — parent to child: marks a component property as an input that can be set by a parent component via property binding. Child component: @Input() title: string = ""; @Input("productId") id: number = 0; // alias. Parent template: <app-card [title]="post.title" [productId]="selectedId">. The value passed from parent flows down to the child's property. @Output() — child to parent: marks a component property as an output that emits events to the parent via EventEmitter. Child component: @Output() itemSelected = new EventEmitter<Product>(); selectItem(product: Product) { this.itemSelected.emit(product); }. Parent template: <app-product-list (itemSelected)="onProductSelected($event)">. $event: in the parent template, $event refers to the emitted value. Two-way binding with @Input + @Output: combine a property input and an event output with Change suffix: @Input() value = ""; @Output() valueChange = new EventEmitter<string>(); — allows parent to use [(value)]="parentProp" (banana-in-a-box syntax). Required inputs (Angular 16+): @Input({ required: true }) label!: string; — compile-time error if parent doesn't provide the value. Input transforms (Angular 16+): @Input({ transform: coerceBooleanProperty }) disabled = false;

Open this question on its own page
18

What is @ViewChild and @ContentChild in Angular?

@ViewChild gives a component access to a child component, directive, or DOM element in its own template (view). @ContentChild accesses content projected into the component via <ng-content>. @ViewChild usage: @ViewChild("myInput") myInputRef!: ElementRef; @ViewChild(ChildComponent) childComp!: ChildComponent; @ViewChild(NgForm) form!: NgForm;. The argument can be: a template reference variable name (string), a component/directive type, or a provider token. Static option: @ViewChild("ref", { static: true }) — resolves before change detection (accessible in ngOnInit); static: false (default) — resolves after change detection (accessible in ngAfterViewInit). Access timing: for static: false, the ViewChild is available in ngAfterViewInit(), NOT in ngOnInit. Accessing before this returns undefined. @ViewChildren: access a QueryList of ALL matching elements: @ViewChildren(ChildComponent) children!: QueryList<ChildComponent>;. @ContentChild: @ContentChild(AlertComponent) alert!: AlertComponent; — access a component projected via <ng-content>. Available in ngAfterContentInit(). @ContentChildren: QueryList of all projected matching elements. Direct DOM manipulation via ElementRef: avoid using this.myInputRef.nativeElement.focus() directly when possible — use Renderer2 for cross-platform compatibility (Angular Universal/SSR). Use Angular reactive approaches over DOM manipulation when possible.

Open this question on its own page
19

What is the async pipe in Angular?

The async pipe subscribes to an Observable or Promise and returns its latest emitted value. When the component is destroyed, it automatically unsubscribes from the Observable — preventing memory leaks without manual subscription management. With Observable: // Component: users$ = this.userService.getUsers(); // Template: <ul><li *ngFor="let user of users$ | async">{{ user.name }}</li></ul>. With null check: <div *ngIf="user$ | async as user">{{ user.name }}</div> — the "as" syntax assigns the emitted value to a local template variable, preventing multiple subscriptions. With loading state: <ng-container *ngIf="users$ | async as users; else loading"> <app-user-list [users]="users"></app-user-list> </ng-container> <ng-template #loading><app-spinner></app-spinner></ng-template>. Benefits over manual subscribe: (1) Auto-unsubscribes on destroy; (2) Triggers change detection on new values; (3) Works naturally with OnPush change detection strategy (manual subscribe + property assignment won't trigger OnPush). Multiple uses: using users$ | async multiple times in the same template creates multiple subscriptions. Use the "as" syntax or shareReplay(1) to share one subscription. Async pipe in OnPush: one of the key reasons to use async pipe — it correctly marks the component for check when Observable emits.

Open this question on its own page
20

What is lazy loading in Angular?

Lazy loading defers loading of Angular feature modules until the user navigates to a route that requires them, reducing the initial bundle size and improving startup performance. Without lazy loading: all code for every feature is bundled together and loaded upfront, even if the user never visits those features. With lazy loading: only the AppModule and eagerly-loaded features are loaded initially; other modules are loaded on-demand. Setting up lazy loading: create a feature module with its own routing: const routes: Routes = [ { path: "admin", loadChildren: () => import("./admin/admin.module").then(m => m.AdminModule) }, { path: "shop", loadComponent: () => import("./shop/shop.component").then(m => m.ShopComponent) // standalone component lazy loading } ];. Preloading strategies: RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules }) — preloads all lazy modules in the background after initial load (best of both worlds: fast initial load + instant navigation). Custom preloading: selectively preload modules based on data attributes. Code splitting: Angular CLI automatically creates separate JavaScript chunks for each lazy-loaded module. Verify in network tab: the admin.module.js file loads only when navigating to /admin. Standalone component lazy loading: Angular 14+ supports lazy loading individual standalone components without modules: loadComponent: () => import("./feature.component").then(m => m.FeatureComponent).

Open this question on its own page
21

What are Angular guards?

Angular route guards are interfaces that control access to routes. They can prevent navigation to a route, prevent navigation away from a route, or preload data for a route. Types: (1) CanActivate: determines if a route can be activated. Used for authentication: return true to allow, false or UrlTree to redirect. canActivate: [AuthGuard]; (2) CanActivateChild: same as CanActivate but for child routes; (3) CanDeactivate: determines if you can leave a route. Used for "unsaved changes" warnings — return false to stay, true to leave. Generic: CanDeactivate<ComponentWithUnsavedChanges>; (4) CanLoad / CanMatch (Angular 15+): determines if a lazy module can be loaded. Prevents downloading the module code if the user shouldn't have access. Modern functional guards (Angular 14+): guards can now be plain functions instead of classes: export const authGuard: CanActivateFn = (route, state) => { return inject(AuthService).isLoggedIn() ? true : inject(Router).createUrlTree(["/login"]); };. Using guards in routes: { path: "dashboard", component: DashboardComponent, canActivate: [authGuard] }. AuthGuard example: check if user is authenticated → if yes, return true → if no, navigate to login with return URL: return this.router.createUrlTree(["/login"], { queryParams: { returnUrl: state.url } }). Resolvers: not technically guards but route lifecycle hook that pre-fetches data before navigation completes: resolve: { userData: UserResolver }.

Open this question on its own page
22

What is Angular interceptor?

An HTTP interceptor intercepts all outgoing HTTP requests and incoming responses in Angular, allowing you to transform or handle them in a centralized location. Common uses: adding auth headers, logging, error handling, caching, request transformation. Creating an interceptor (class-based): @Injectable() export class AuthInterceptor implements HttpInterceptor { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { const token = this.authService.getToken(); if (token) { req = req.clone({ headers: req.headers.set("Authorization", "Bearer " + token) }); } return next.handle(req); } }. Key points: (1) HttpRequest is immutable — clone with modifications; (2) Must call next.handle(req) to pass to the next interceptor or to the server; (3) Return value is an Observable of HttpEvents. Registering: in AppModule providers: { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }. The multi: true adds to an array of interceptors (not replacing). Multiple interceptors form a chain — execution order is the registration order for request, reverse for response. Functional interceptors (Angular 15+): export const authInterceptor: HttpInterceptorFn = (req, next) => { return next(req.clone({ headers: req.headers.set("Authorization", "Bearer " + token) })); };. Register: provideHttpClient(withInterceptors([authInterceptor])).

Open this question on its own page
23

What is Angular Material?

Angular Material is the official component library for Angular, implementing Google's Material Design specification. It provides a comprehensive set of UI components that are accessible, responsive, and well-tested. Install: ng add @angular/material — configures theme, typography, animations automatically. Component categories: Form controls: Autocomplete, Checkbox, Datepicker, Form field, Input, Radio, Select, Slider, Slide toggle; Navigation: Menu, Sidenav, Toolbar; Layout: Card, Divider, Expansion panel, Grid list, List, Stepper, Tabs, Tree; Buttons/Indicators: Button, Button toggle, Badge, Chips, Icon, Progress bar, Progress spinner; Popups/Modals: Bottom sheet, Dialog, Snack bar, Tooltip; Data table: Paginator, Sort, Table. Usage: import the specific module for each component: import { MatButtonModule } from "@angular/material/button";. Theming: Angular Material supports custom themes with 3-5 palettes (primary, accent, warn). Theming uses CSS custom properties (Material Design 3 / Material You). CDK (Component Dev Kit): the underlying primitives Angular Material is built on — available for building custom components: overlays, drag-and-drop, virtual scrolling, accessibility utilities. Angular Flex Layout: (deprecated — use CSS Flexbox/Grid directly). Angular CDK Virtual Scroll: cdk-virtual-scroll-viewport — efficiently renders large lists by only rendering visible items.

Open this question on its own page
24

What is Zone.js in Angular?

Zone.js is a library that creates execution contexts called "zones" — it patches all browser async APIs (setTimeout, setInterval, Promise, XHR, event listeners, etc.) to intercept when they are called and when they complete. Angular uses Zone.js to automatically know when to run change detection. How it works: Angular creates one zone (NgZone) for the application. When an async operation (click event, HTTP response, timer) completes, Zone.js notifies Angular, which then triggers change detection across the component tree. Without Zone.js: developers would need to manually call ChangeDetectorRef.detectChanges() or markForCheck() after every async operation to update the view. Drawbacks of Zone.js: (1) Triggers change detection very broadly — any async event, even unrelated to UI, triggers the whole tree check; (2) Increases bundle size (~70KB minified+gzip); (3) Can cause "ExpressionChangedAfterItHasBeenCheckedError" in dev mode. Running code outside Angular's zone: this.ngZone.runOutsideAngular(() => { performanceHeavyOperation(); }) — prevents change detection for this operation. Re-enter to update UI: this.ngZone.run(() => { this.updateUI(); }). Zoneless Angular (future): Angular is working on making Zone.js optional. Angular 17 introduced experimental zoneless support using Signals for fine-grained change detection. provideExperimentalZonelessChangeDetection() in main.ts.

Open this question on its own page
25

What is an Angular module vs a standalone component?

Angular has two paradigms for organizing code: NgModule-based and standalone. NgModule approach (traditional): components must be declared in an NgModule. To use another component, import its module. Modules bundle declarations, imports, exports, and providers together. Every component, directive, and pipe must belong to exactly one module. This provides clear boundaries but requires boilerplate. Standalone approach (Angular 14+): components, directives, and pipes can be "standalone" — they don't belong to an NgModule and directly declare their own dependencies. @Component({ standalone: true, selector: "app-profile", template: "...", imports: [CommonModule, RouterModule, MatButtonModule, UserCardComponent] }). The imports array in @Component replaces NgModule's imports — you import directly what you need. Benefits of standalone: (1) Less boilerplate (no module file); (2) Better tree-shaking (only import what you use); (3) Simpler lazy loading (load a component directly, not a module); (4) Clearer dependency graph; (5) Easier to understand component dependencies. Bootstrapping a standalone app: bootstrapApplication(AppComponent, { providers: [provideRouter(routes), provideHttpClient()] }). Migration: run ng generate @angular/core:standalone to automatically migrate NgModule components to standalone. Interoperability: standalone and module-based code work side by side — a standalone component can be declared in an NgModule, and a standalone component can import NgModules.

Open this question on its own page
26

What is the Angular compilation process (JIT vs AOT)?

Angular templates must be compiled into JavaScript. Two modes: JIT (Just-In-Time) compilation: templates are compiled in the browser at runtime, right before execution. Used in development with ng serve. Pros: faster build time (no compile step during development), easier debugging (source maps for templates). Cons: larger bundle (compiler included in the bundle), slower application startup (compilation happens at runtime), reveals template errors only at runtime. AOT (Ahead-Of-Time) compilation: templates are compiled to optimized JavaScript at build time — before the browser downloads or runs the code. Used in production with ng build --configuration production. Pros: (1) Faster application startup (no compilation in browser); (2) Smaller bundle (compiler not shipped); (3) Template errors caught at build time (not runtime); (4) Improved security (templates compiled to JavaScript, no injection of HTML/script); (5) Better tree-shaking. Cons: slower build time. Ivy compiler (Angular 9+): Angular's current compilation and rendering pipeline, replacing the older View Engine. Ivy enables: (1) Smaller bundles (locality principle — each component is self-sufficient); (2) Faster testing (no TestBed bootstrap overhead); (3) Incremental DOM compilation; (4) Better debugging (component instances visible in DevTools). Since Angular 9, Ivy is the default. Angular 12+ removed View Engine entirely. AOT is the default for production builds; Angular 9+ also uses AOT in development by default.

Open this question on its own page
27

What are Angular resolvers?

Resolvers are route lifecycle hooks that pre-fetch data before a route is activated — the component only renders after the data is available. This avoids the pattern of showing a component with empty data then loading. Traditional class resolver: @Injectable({ providedIn: "root" }) export class UserResolver implements Resolve<User> { constructor(private userService: UserService) {} resolve(route: ActivatedRouteSnapshot): Observable<User> { return this.userService.getUser(route.params["id"]); } }. Register in routes: { path: "users/:id", component: UserDetailComponent, resolve: { user: UserResolver } }. Access resolved data in component: constructor(private route: ActivatedRoute) {} ngOnInit() { this.user = this.route.snapshot.data["user"]; // or Observable: this.route.data.subscribe(data => this.user = data["user"]); }. Functional resolver (Angular 14+): simpler approach without class: export const userResolver: ResolveFn<User> = (route, state) => { return inject(UserService).getUser(route.params["id"]); };. Error handling in resolvers: if the Observable errors, navigation is cancelled. Use catchError to redirect or provide default data. Pros: data available immediately in ngOnInit, no loading state needed in component template. Cons: navigation appears "stuck" while resolving — adds perceived latency. For better UX with longer loads, show a progress indicator and let the component handle loading state itself.

Open this question on its own page
28

What is Angular Universal (SSR)?

Angular Universal (now integrated into Angular as @angular/ssr) enables Server-Side Rendering (SSR) of Angular applications — the app renders to HTML on the server and sends the pre-rendered HTML to the client. Why SSR? (1) SEO: search engine crawlers receive fully rendered HTML, not an empty shell with JavaScript; (2) Performance: users see content faster (First Contentful Paint) — the browser shows HTML immediately without waiting for JavaScript to execute; (3) Social sharing: Open Graph meta tags are pre-rendered for proper social media cards. Setting up SSR: ng add @angular/ssr — adds server-side rendering with Express.js. Creates server.ts (Express server), app.config.server.ts (server config), and updates build configuration. Hydration: after SSR HTML is displayed, Angular "hydrates" it — attaches event handlers and activates the Angular app in the browser. Without hydration, Angular re-creates the DOM. Non-destructive hydration (Angular 16+): Angular can reuse the server-rendered DOM instead of recreating it — reduces flickering and improves LCP metric. Platform-specific code: some APIs (localStorage, window, document) don't exist on the server. Use isPlatformBrowser/isPlatformServer, or inject the PLATFORM_ID token to guard platform-specific code. Static Site Generation (SSG/prerendering): ng build --prerender — generates static HTML files at build time for all or specific routes.

Open this question on its own page
Intermediate 17 questions

Practical knowledge for developers with hands-on experience.

01

What is RxJS and how is it used in Angular?

RxJS (Reactive Extensions for JavaScript) is a library for composing asynchronous and event-based programs using observable sequences. Angular uses RxJS extensively for HTTP requests, user input handling, routing, and forms. Core concepts: (1) Observable: a stream of values over time — lazy, doesn't execute until subscribed. Cold observable: each subscriber gets its own execution (HTTP requests). Hot observable: shares one execution (UI events, WebSocket); (2) Observer: subscribes to an observable with next (value), error (error), and complete (done) callbacks; (3) Subject: both an Observable and an Observer — multicast (many subscribers). Types: Subject, BehaviorSubject (stores latest value, new subscribers get it immediately), ReplaySubject (stores N values), AsyncSubject (emits only the last value on completion); (4) Operators: pure functions that transform, filter, combine observables. Imported from "rxjs/operators". Key operators: Transformation: map, switchMap, mergeMap, concatMap, exhaustMap; Filtering: filter, take, takeUntil, debounceTime, distinctUntilChanged, skip; Combination: combineLatest, merge, zip, forkJoin, withLatestFrom; Error handling: catchError, retry, retryWhen; Utility: tap, delay, shareReplay, startWith, scan. Avoiding memory leaks: always unsubscribe: use takeUntil with a destroy$ Subject, take(1) for single-value streams, or async pipe (auto-unsubscribes). Angular-specific: EventEmitter extends Subject; ActivatedRoute params is an Observable; form value changes is an Observable; HttpClient returns Observables.

Open this question on its own page
02

What is the difference between switchMap, mergeMap, concatMap, and exhaustMap?

These are higher-order mapping operators in RxJS that transform a value into an Observable and subscribe to it. They differ in how they handle multiple concurrent inner Observables: switchMap: when a new outer value arrives, cancels the current inner Observable and switches to a new one. Use when only the latest value matters (autocomplete search — cancel previous searches when user types again). searchTerm$.pipe(switchMap(term => this.http.get("/search?q=" + term))). mergeMap (flatMap): allows multiple inner Observables to run concurrently. Does not cancel — all complete eventually. Use when order doesn't matter and concurrency is desired (upload multiple files simultaneously). Risk: order of results is non-deterministic. concatMap: queues inner Observables — each new one starts only after the previous completes. Preserves order. Use when order matters and operations must be sequential (process tasks in order, save operations that depend on previous). Slowest — no parallelism. exhaustMap: ignores new outer values while an inner Observable is active. Use to prevent duplicate submissions (form submit button — ignore clicks while request is in flight). submitClick$.pipe(exhaustMap(() => this.http.post("/api/submit", data))). Memory aid: switch = cancel latest; merge = parallel; concat = sequential queue; exhaust = ignore while busy.

Open this question on its own page
03

What is the difference between Subject, BehaviorSubject, ReplaySubject, and AsyncSubject?

All four are types of Subject in RxJS — they are both Observable and Observer (can emit values AND be subscribed to). They differ in how they handle late subscribers: Subject: no state — late subscribers don't receive previously emitted values. Only emits to current subscribers. Use for event buses, button clicks, WebSocket messages (no need for historical values). const subject = new Subject<number>(); subject.next(1); subject.subscribe(v => console.log(v)); // won't see 1 subject.next(2); // subscriber sees 2. BehaviorSubject: stores the CURRENT value — new subscribers immediately receive the current value. Requires initial value. Use for state that needs to be available immediately (current user, auth state, theme). const user$ = new BehaviorSubject<User|null>(null); // current state user$.getValue(). ReplaySubject(n): buffers the last N emitted values — new subscribers receive the buffered history. Use for caching/replaying recent values (last N chat messages, recent notifications). const replay = new ReplaySubject<number>(3);. AsyncSubject: emits only the LAST value, and only when the Subject completes. Used rarely — for operations that produce one final result when done (similar to Promise). Choosing: most state management = BehaviorSubject; one-time events = Subject; need last N values = ReplaySubject(N).

Open this question on its own page
04

What is the Angular NgRx state management library?

NgRx is Angular's implementation of the Redux pattern using RxJS. It provides a reactive state management solution for complex Angular applications. Core principles: single immutable state tree, state changed only by pure functions (reducers), all state changes flow through dispatched actions. Core concepts: (1) Store: the single source of truth — a RxJS BehaviorSubject holding the entire app state. Access: this.store.select(selectUsers); (2) Actions: descriptive events that describe what happened: export const loadUsers = createAction("[Users] Load Users"); export const loadUsersSuccess = createAction("[Users] Load Users Success", props<{ users: User[] }>());; (3) Reducers: pure functions that compute the new state: const usersReducer = createReducer(initialState, on(loadUsersSuccess, (state, { users }) => ({ ...state, users, loading: false })));; (4) Selectors: memoized functions to derive state slices: export const selectUsers = createSelector(selectUsersState, state => state.users);; (5) Effects: handle side effects (HTTP calls) — listen for actions, perform async operations, dispatch new actions: loadUsers$ = createEffect(() => this.actions$.pipe(ofType(loadUsers), switchMap(() => this.http.get<User[]>("/api/users").pipe(map(users => loadUsersSuccess({ users })), catchError(error => of(loadUsersFailure({ error }))))))));. When to use: complex state shared across many components, complex state transitions, need for time-travel debugging. For simpler state, use Angular services with BehaviorSubject.

Open this question on its own page
05

What is Angular lazy loading and preloading strategy?

Lazy loading splits the application into multiple JavaScript bundles and loads them on-demand when the user navigates to specific routes. Implementation: use loadChildren (for modules) or loadComponent (for standalone components): { path: "admin", loadChildren: () => import("./admin/admin.module").then(m => m.AdminModule) }. The build produces separate chunk files. Preloading strategies determine which lazy modules are pre-downloaded after initial load: (1) NoPreloading (default): modules load only when navigated. First visit to /admin has a loading delay; (2) PreloadAllModules: after initial app loads, background-download all lazy modules. Good if the app isn't too large: RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules }). First navigation to /admin is instant; (3) Custom PreloadingStrategy: selectively preload based on route data or other conditions. Implement PreloadingStrategy interface: only preload routes with data: { preload: true }. Good for large apps where PreloadAllModules wastes bandwidth; (4) QuicklinkStrategy: preload links visible in the current viewport (IntersectionObserver) — preloads what the user is likely to click next. Third-party library ngx-quicklink. Bundle analysis: ng build --stats-json then npx webpack-bundle-analyzer dist/stats.json to visualize chunk sizes. Aim for: initial bundle <200KB gzipped, lazy chunks proportional to feature complexity.

Open this question on its own page
06

What is Angular change detection with OnPush strategy?

The OnPush change detection strategy (ChangeDetectionStrategy.OnPush) is an optimization that restricts when Angular checks a component for changes, reducing the number of DOM updates. Default strategy (CheckAlways): Angular checks ALL components every time ANY async event fires (click, timer, HTTP response). For large component trees, this is expensive. OnPush triggers: a component with OnPush is only checked when: (1) An @Input() reference changes (primitive value changes or new object/array reference — mutation doesn't trigger); (2) An event originated from the component or its children (user click inside the component); (3) An Observable used with the async pipe emits a new value; (4) changeDetectorRef.markForCheck() is called manually. Implementation: @Component({ changeDetection: ChangeDetectionStrategy.OnPush }). Immutability requirement: OnPush only detects reference changes. You must create new arrays/objects instead of mutating: this.items = [...this.items, newItem] (triggers) vs this.items.push(newItem) (doesn't trigger). Use spread operator, Object.assign(), or immutable libraries. Manual triggering: when a service changes data (not through an Observable), use: this.cdr.markForCheck() — schedules the component for check in the next cycle; this.cdr.detectChanges() — runs change detection immediately synchronously. Signals (Angular 16+): the new reactivity model — fine-grained change detection that doesn't require OnPush manually; signals automatically track which views depend on which signals.

Open this question on its own page
07

What are Angular Signals?

Signals are Angular's new reactive primitives introduced in Angular 16 (stable in 17), providing a simpler and more efficient way to manage state and reactivity. A signal is a wrapper around a value that notifies consumers when the value changes — enabling fine-grained reactivity without Zone.js. Creating signals: const count = signal(0); // WritableSignal<number>. Reading signals: call as a function: count() — reactive reads in templates/computed. Writing signals: count.set(5) — replace value; count.update(v => v + 1) — compute from previous; count.mutate(obj => obj.name = "Alice") — mutate in-place (for objects). Computed signals: derived values that auto-update: const doubled = computed(() => count() * 2) — lazily computed, cached, only re-evaluated when dependencies change. Effects: side effects that run when signals change: const effectRef = effect(() => { console.log("Count is: " + count()); }); — auto-tracks signal dependencies inside; runs after every change. In templates: use signals directly: {{ count() }} — Angular automatically marks the view for check when the signal changes. Signal-based inputs/outputs (Angular 17.1+): @Input({ required: true }) title = input.required<string>(); returns a Signal. Benefits over traditional change detection: fine-grained updates (only views reading a signal update), no need for OnPush, no Zone.js dependency, simpler mental model. Signals can interoperate with Observables using toObservable() and toSignal().

Open this question on its own page
08

What is Angular content projection with ng-content?

Content projection allows a component to receive and render HTML content from its parent, making components more flexible and reusable. The component defines slot(s) where the projected content appears using <ng-content>. Basic projection: component template: <div class="card"> <div class="card-header">{{ title }}</div> <div class="card-body"> <ng-content></ng-content> </div> </div>. Usage: <app-card [title]="heading"> <p>This content is projected into the card body.</p> </app-card>. Multi-slot projection: use the select attribute to project content into named slots: <ng-content select="[card-header]"></ng-content> <ng-content select=".card-body-content"></ng-content> <ng-content></ng-content> <!-- fallback -->. Usage: <app-card> <h2 card-header>Title</h2> <p class="card-body-content">Body</p> </app-card>. ng-content with component selector: <ng-content select="app-header"> — project only app-header components. ngProjectAs: makes an element appear as if it has a different selector for ng-content matching. Accessing projected content: use @ContentChild / @ContentChildren in the component class. ngTemplateOutlet: project template references: <ng-container *ngTemplateOutlet="headerTemplate"> — more dynamic than ng-content.

Open this question on its own page
09

What are Angular pipes — pure vs impure?

Angular pipes transform data for display. The critical performance distinction is between pure and impure pipes: Pure pipes (default): called only when Angular detects a pure change to the input value — a change to a primitive (string, number, boolean) or a change in object/array reference. Angular caches the result and reuses it until the input reference changes. Impure pipes: called on every change detection cycle — even if the input reference hasn't changed (e.g., if an element was added to an array without creating a new array). Declared with pure: false: @Pipe({ name: "myFilter", pure: false }). Why impure pipes are dangerous: if a pipe is impure and change detection runs 60 times/second during animations, the pipe is called 60 times/second. This can severely impact performance. The AsyncPipe is impure — it must be impure to react when an Observable emits a new value (the Observable reference doesn't change, but its emitted value does). When to use impure: only when you need the pipe to react to changes within a mutable reference (like adding to an array). Better alternative: use the OnPush strategy, work with immutable data (always create new arrays), and keep pipes pure. Custom pipe example: @Pipe({ name: "filterUsers", pure: false }) export class FilterUsersPipe implements PipeTransform { transform(users: User[], searchTerm: string): User[] { return users.filter(u => u.name.includes(searchTerm)); } } — impure because users array may be mutated. Better: use users$ | async | filterUsers:term where users$ emits a new array each time.

Open this question on its own page
10

How does Angular handle memory leaks and subscription management?

Memory leaks in Angular commonly occur from unsubscribed Observables — subscriptions that outlive the component. When the component is destroyed, active subscriptions continue running and hold references to the component, preventing garbage collection. Problem: ngOnInit() { this.router.events.subscribe(e => this.doSomething(e)); // never unsubscribed! } — after component destroy, subscriber still exists. Solutions: (1) async pipe (preferred): template uses {{ data$ | async }} — auto-unsubscribes on destroy. Best for templates; (2) takeUntil pattern: private destroy$ = new Subject<void>(); ngOnInit() { this.dataService.data$.pipe(takeUntil(this.destroy$)).subscribe(data => this.data = data); } ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); }. takeUntil completes the subscription when destroy$ emits; (3) take(1): auto-completes after first value: this.route.params.pipe(take(1)).subscribe(params => { this.id = params["id"]; }). Use when you only need the value once; (4) Subscription + unsubscribe: private sub = new Subscription(); ngOnInit() { this.sub.add(stream1.subscribe(...)); this.sub.add(stream2.subscribe(...)); } ngOnDestroy() { this.sub.unsubscribe(); }; (5) Angular 16+ takeUntilDestroyed: import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; stream$.pipe(takeUntilDestroyed()) — works outside lifecycle hooks too (in constructors); (6) toSignal(): auto-manages subscription when signal is used in a component.

Open this question on its own page
11

What is Angular Reactive Forms in depth?

Angular Reactive Forms give explicit, programmatic control over form structure and validation. Core classes: FormControl: individual form field: new FormControl("initialValue", [Validators.required, Validators.email]). Properties: value, valid, invalid, dirty (user changed), touched (user blurred), errors. FormGroup: group of related controls: new FormGroup({ email: new FormControl("", Validators.required), password: new FormControl("") }). Access control: form.get("email")?.value. FormArray: ordered list of controls (dynamic fields): new FormArray([new FormControl(""), new FormControl("")]); array.push(new FormControl(""));. FormBuilder: shorthand for creating form structures: this.fb.group({ email: ["", [Validators.required, Validators.email]], tags: this.fb.array([]) }). Built-in validators: Validators.required, Validators.email, Validators.minLength(8), Validators.maxLength(50), Validators.pattern(/regex/), Validators.min(0), Validators.max(100). Custom validators: synchronous: (control: AbstractControl): ValidationErrors|null => control.value === "forbidden" ? { forbidden: true } : null; asynchronous: returns Observable<ValidationErrors|null> (for server-side validation — unique email check). Cross-field validation: apply a validator to the group: this.fb.group({ password: "", confirm: "" }, { validators: passwordMatchValidator }). updateOn: control when validation fires: new FormControl("", { updateOn: "blur" }) — validates on blur, not every keystroke. Value changes: form.get("email")!.valueChanges.pipe(debounceTime(300)).subscribe(v => this.checkEmail(v)).

Open this question on its own page
12

What is Angular i18n (internationalization)?

Angular's built-in internationalization (i18n) support enables creating multilingual applications. Marking text for translation: add the i18n attribute to elements: <h1 i18n="@@welcomeTitle">Welcome</h1>. The @@ prefix defines a custom ID. For attributes: <input i18n-placeholder="@@searchPlaceholder" placeholder="Search">. Workflow: (1) Mark content in templates with i18n; (2) Extract messages: ng extract-i18n --output-path src/locale — generates messages.xlf (XLIFF format); (3) Create locale-specific files: messages.fr.xlf, messages.es.xlf; (4) Translators fill in the translations; (5) Build for each locale: ng build --localize — produces separate builds per locale. Configuration in angular.json: "i18n": { "sourceLocale": "en-US", "locales": { "fr": "src/locale/messages.fr.xlf" } }, "build": { "options": { "localize": true } }. Runtime i18n (Angular 9+): serve different locale builds from different URLs. Deploy multiple builds (en/, fr/) and route based on browser preference or user selection. Third-party alternatives: ngx-translate — runtime translation switching without multiple builds; transloco — modern, powerful i18n library with lazy loading of translation files. Angular's built-in i18n produces optimal bundles per locale; ngx-translate/transloco allow runtime language switching from one build.

Open this question on its own page
13

What is Angular animation?

Angular's animation system is built on the Web Animations API and CSS transitions, providing a powerful DSL for defining component animations in TypeScript. Import: import { trigger, state, style, animate, transition, keyframes, query, stagger } from "@angular/animations";. Core concepts: trigger: gives the animation a name for binding in templates: @Component({ animations: [ trigger("fadeSlide", [ ... ]) ] }). Template binding: <div [@fadeSlide]="animationState">; state: defines styles for a named state: state("void", style({ opacity: 0, transform: "translateY(-20px)" })). void = element not in DOM; transition: defines animation between states: transition("void => *", animate("300ms ease-out")) (* = any state). Shorthand: transition(":enter", ...) (void → *), transition(":leave", ...) (* → void); animate: specifies duration, easing, delay: animate("300ms 100ms ease-in-out", style({ opacity: 1 })); keyframes: step-by-step animations; query/stagger: animate child elements with staggered timing. Common animations: enter/leave fade: add the trigger to the element, bind to a boolean. List item stagger animations with @for. Route transition animations: wrap router-outlet in a trigger based on route data. Angular Animations vs CSS: Angular animations are more programmatic (state-driven), better for conditional animations, and work with component data. CSS animations are simpler for basic effects.

Open this question on its own page
14

What is the Angular HttpClient interceptor and error handling?

Centralized HTTP error handling in Angular uses interceptors to catch and handle errors from all HTTP requests. Error types: Client-side errors: network issues, CORS, browser preventing the request; Server-side errors: 4xx (client errors) and 5xx (server errors) status codes. Error interceptor implementation: export class ErrorInterceptor implements HttpInterceptor { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { return next.handle(req).pipe( catchError((error: HttpErrorResponse) => { if (error.status === 401) { this.authService.logout(); this.router.navigate(["/login"]); } else if (error.status === 403) { this.router.navigate(["/forbidden"]); } else if (error.status >= 500) { this.notificationService.showError("Server error. Please try again."); } const errorMessage = error.error?.message || error.message || "Unknown error"; return throwError(() => new Error(errorMessage)); }) ); } }. Retry logic: return next.handle(req).pipe( retry({ count: 3, delay: 1000 }), catchError(...) ). Token refresh: on 401, refresh the token and retry: pipe with catchError(err => err.status === 401 ? this.authService.refreshToken().pipe(switchMap(token => next.handle(addToken(req, token)))) : throwError(() => err)). Component-level error handling: override interceptor behavior: this.http.get("/api/data").pipe(catchError(err => { this.localError = err; return EMPTY; })).

Open this question on its own page
15

What is Angular Standalone API?

The Standalone API (Angular 14+, stable Angular 15+) is a major simplification of Angular's architecture, reducing boilerplate by eliminating the requirement for NgModules. Standalone components: @Component({ standalone: true, selector: "app-user-card", template: "...", imports: [CommonModule, RouterModule, MatButtonModule] }). The standalone: true flag makes the component self-contained — it declares its own imports directly. Bootstrapping a standalone app: no AppModule — use bootstrapApplication in main.ts: bootstrapApplication(AppComponent, { providers: [ provideRouter(routes), provideHttpClient(withInterceptorsFromDi()), importProvidersFrom(MatDialogModule), { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true } ] });. Standalone directives and pipes: @Directive({ standalone: true, selector: "[appHighlight]" }). Lazy loading standalone components: { path: "profile", loadComponent: () => import("./profile.component").then(m => m.ProfileComponent) }. Benefits over NgModules: (1) Less boilerplate (no declarations/exports arrays); (2) Explicit dependencies (each component declares exactly what it uses); (3) Better tree-shaking; (4) Easier testing (no TestBed.configureTestingModule module setup); (5) Simpler mental model. Interoperability: standalone components can be used inside NgModule-based apps and vice versa. Migration: ng generate @angular/core:standalone schematics automate migration from NgModule-based to standalone. Angular 17 defaults to standalone for new projects.

Open this question on its own page
16

What is Angular environment configuration?

Angular's environment configuration system allows different settings for development, staging, and production builds. Traditional approach (pre-Angular 15): create environment files: src/environments/environment.ts (dev): export const environment = { production: false, apiUrl: "http://localhost:3000/api" };. src/environments/environment.prod.ts: export const environment = { production: true, apiUrl: "https://api.example.com" };. Configure file replacements in angular.json: "configurations": { "production": { "fileReplacements": [{ "replace": "src/environments/environment.ts", "with": "src/environments/environment.prod.ts" }] } }. During ng build --configuration production, Angular replaces environment.ts with environment.prod.ts. Angular 15+ approach: environment files still exist but can be omitted in favor of: Application configuration via tokens: inject the API URL via InjectionToken: const API_URL = new InjectionToken<string>("API_URL"); providers: [{ provide: API_URL, useValue: environment.apiUrl }]. Runtime configuration: load config via an HTTP call at startup using APP_INITIALIZER: providers: [{ provide: APP_INITIALIZER, useFactory: (configService: ConfigService) => () => configService.load(), deps: [ConfigService], multi: true }]. This enables configuration without rebuilding (Docker-friendly — inject at deployment time). Best practice: never put secrets in environment files — they end up in the client-side bundle. Use environment variables only for public config (API URLs, feature flags).

Open this question on its own page
17

What is Angular testing with Jasmine and Karma?

Angular uses Jasmine (testing framework) + Karma (test runner) by default for unit testing. Component tests use TestBed to create a test Angular environment. Basic component test: describe("UserCardComponent", () => { let component: UserCardComponent; let fixture: ComponentFixture<UserCardComponent>; beforeEach(async () => { await TestBed.configureTestingModule({ imports: [UserCardComponent] // standalone; or declarations for module-based }).compileComponents(); fixture = TestBed.createComponent(UserCardComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it("should display username", () => { component.user = { name: "Alice" }; fixture.detectChanges(); const nameEl = fixture.debugElement.query(By.css(".name")); expect(nameEl.nativeElement.textContent).toContain("Alice"); }); });. Testing services: inject via TestBed: TestBed.inject(UserService). Mock dependencies: const mockHttp = jasmine.createSpyObj("HttpClient", ["get"]); mockHttp.get.and.returnValue(of([{ name: "Alice" }]));. Testing HTTP: use HttpClientTestingModule and HttpTestingController to mock HTTP requests. Testing Observables: use the async helper or fakeAsync + tick(). spyOn: spyOn(service, "getUsers").and.returnValue(of([]));. Angular 16+ testing with Jest: ng add @angular-builders/jest — replace Karma with Jest for faster test execution, better DX, snapshot testing. Running tests: ng test — watch mode; ng test --code-coverage — generate coverage report.

Open this question on its own page
Advanced 9 questions

Deep expertise questions for senior and lead roles.

01

What is Angular Ivy and how does it improve Angular?

Ivy is Angular's next-generation compilation and rendering engine, introduced experimentally in Angular 8 and made the default in Angular 9. It replaced the previous View Engine (VE). Core Ivy principles: (1) Locality: Ivy compiles each component independently (no global knowledge needed) — a component's compiled output is self-sufficient. View Engine needed global analysis. This enables incremental builds and faster compilation; (2) Tree-shakable: Ivy's generated code only references what it uses. With View Engine, even unused Angular features were bundled. Ivy + tree-shaking produces much smaller bundles (Hello World app went from ~36KB to ~4KB gzipped); (3) Instruction-based rendering: Ivy generates sequences of instructions (like a mini-bytecode) instead of factory functions. Easier to optimize; (4) Improved debugging: component instances are directly attached to DOM nodes — visible in DevTools. ng.getComponent(element) in console; (5) Better testing: components can be loaded in TestBed more independently without bootstrapping large module trees — faster tests; (6) Simpler metadata: component metadata is processed at compile time, reducing runtime overhead; (7) Dynamic imports for lazy loading: Ivy enables lazy loading of individual standalone components (not just modules); (8) New template type checking: stricter type checking in templates — catches more errors at compile time. Angular 12 removed View Engine entirely. Angular 14+ standalone components are fully Ivy-native.

Open this question on its own page
02

How does Angular handle server-side rendering (SSR) with Hydration?

Angular Universal's SSR renders the app on the server, sending HTML to the browser. Angular 16 introduced non-destructive hydration — a major improvement over the previous approach. Old approach (without hydration): server sends pre-rendered HTML → browser displays it immediately → Angular bootstraps and completely DESTROYS the server-rendered DOM and recreates it from scratch using client-side JavaScript. This caused flicker, lost scroll position, and wasted the SSR effort for initial content. Non-destructive hydration (Angular 16+): server sends pre-rendered HTML + serialized state (TransferState) → browser displays HTML immediately → Angular bootstraps and REUSES the existing DOM (doesn't recreate) → attaches event handlers to existing DOM nodes → app becomes interactive. The DOM is preserved — no flicker. Enable: bootstrapApplication(AppComponent, { providers: [provideClientHydration()] }). TransferState: transfer server-computed data (HTTP responses, computed values) to the client to avoid duplicate HTTP requests. Server caches responses in TransferState; client reads the cache before making HTTP calls. HttpClient automatically uses TransferState when TransferState is available. Incremental hydration (Angular 17+, experimental): defer hydration of specific parts of the page until they're needed (e.g., components in @defer blocks). Reduces JS execution on initial load. Challenges with SSR: guard server-specific code (no window/document/localStorage on server); handle authentication (cookies vs tokens); third-party libraries may not be SSR-compatible. Performance impact: SSR with hydration dramatically improves Core Web Vitals (LCP, FID, CLS).

Open this question on its own page
03

What is Angular's dependency injection hierarchical injector system?

Angular's DI system uses a hierarchical injector tree that mirrors the component tree, enabling powerful scoping of dependencies. Injector hierarchy (root to leaf): (1) Platform injector: highest level, platform-wide singletons. Services provided here persist across Angular app restarts (rarely used); (2) Root injector (AppModule / bootstrapApplication): services registered here are app-wide singletons. Most services live here via providedIn: "root"; (3) NgModule injectors: lazy-loaded modules get their own injector. Services provided here are scoped to the module; (4) Element injectors: each component/directive has its own injector. Services in @Component({ providers: [] }) create a new instance for that component subtree. Resolution order: when a token is requested, Angular walks up the injector hierarchy: component → parent component → ... → module → root → platform. First match wins. Providing at component level: @Component({ providers: [SessionService] }) — each instance of this component gets its own SessionService instance (not a singleton). Useful for wizard steps, list items with their own state. ViewProviders vs Providers: viewProviders — available only to this component and its view children (NOT content children via ng-content); providers — available to component and ALL children (view + content). @SkipSelf(): skip the current injector, start from parent. @Self(): only look in current injector. @Optional(): inject null if not found (no error). @Host(): stop at the host element injector.

Open this question on its own page
04

What are Angular Signals in detail and how do they compare to RxJS?

Angular Signals provide synchronous reactive primitives optimized for UI state. Comparing Signals vs RxJS: Synchronous vs Asynchronous: Signals are synchronous — reading a signal returns its current value immediately. RxJS Observables are async push-based — you subscribe and values arrive over time. When to use Signals: local component state, derived computed values, form field values, UI state (is sidebar open?), anything that's "current state now." When to use RxJS: async operations (HTTP, WebSocket, timers), event streams, combining multiple async sources, complex async orchestration (retry, debounce, switchMap). Interoperability: toSignal(observable$) — wraps an Observable as a Signal (auto-subscribes, requires injection context); toObservable(signal) — wraps a Signal as an Observable (emits when signal changes). Signal-based component inputs (Angular 17.1+): class Component { title = input.required<string>(); count = input(0); // with default onClick = output<void>(); model = model(0); // two-way binding signal }. Signal queries (Angular 17.2+): header = viewChild<HeaderComponent>("header"); buttons = viewChildren(ButtonComponent); slot = contentChild<SlotDirective>(SlotDirective); — Signal-based equivalents of @ViewChild/@ContentChild. Effect cleanup: effects can return a cleanup function: effect(() => { const subscription = stream$.subscribe(...); return () => subscription.unsubscribe(); }). Future: Signals will eventually enable Angular to run without Zone.js, enabling truly fine-grained reactivity.

Open this question on its own page
05

What is Angular performance optimization techniques?

Angular performance optimization operates at multiple levels: 1. Change Detection: Use OnPush strategy widely; use Signals (Angular 16+) for fine-grained reactivity; avoid function calls in templates ({{ computedValue }} vs {{ compute() }} — function called every CD cycle); use trackBy in *ngFor: *ngFor="let item of items; trackBy: trackById" (prevents re-rendering unchanged items). 2. Bundle size: lazy load feature routes; use tree-shakeable providers (providedIn: "root"); import only needed parts of libraries (avoid importing full lodash — use specific imports); analyze bundles with webpack-bundle-analyzer; use standalone components (better tree-shaking than NgModules). 3. Rendering: Use virtual scrolling for large lists (CdkVirtualScrollViewport); defer loading non-critical content with @defer blocks; use @defer (on viewport) to load when visible; memoize expensive computations with memo or compute in lifecycle hooks not templates. 4. HTTP/Data: HTTP caching with shareReplay(1); use pagination; implement debounce on search inputs. 5. Assets: SSR/prerendering for initial load performance; service workers (PWA) for offline and caching; preload critical images; lazy-load images with loading="lazy". 6. Bundle optimization: AOT compilation (always in production); Terser for minification; enable source map explorer to identify large dependencies. 7. @defer blocks (Angular 17+): @defer (on idle) { <heavy-component/> } @placeholder { <spinner/> } — load components lazily with triggers: idle, viewport, interaction, hover, timer, when, on.

Open this question on its own page
06

What is the Angular @defer block?

The @defer block (Angular 17+) enables declarative lazy loading of template content and components directly in templates — no JavaScript routing configuration needed. It replaces third-party solutions for progressive loading. Basic syntax: @defer { <heavy-chart-component/> } @placeholder { <p>Chart will appear here</p> } @loading { <mat-spinner/> } @error { <p>Failed to load chart</p> }. Trigger types: @defer (on idle) — loads when browser is idle (requestIdleCallback); @defer (on viewport) — loads when placeholder enters viewport (IntersectionObserver); @defer (on interaction) — loads on first interaction (click, focus, hover) with the placeholder; @defer (on hover) — on hover; @defer (on timer(5s)) — after 5 seconds; @defer (on immediate) — no delay, but still async (component lazy-loaded); @defer (when isAdminUser()) — loads when condition becomes true. Prefetch: separate when to prefetch from when to render: @defer (on interaction; prefetch on idle) { ... } — render on interaction, but start downloading the code while browser is idle. Multiple triggers: @defer (on viewport; on timer(2s)) — loads when EITHER condition is met. Minimum loading time: @loading (after 100ms; minimum 1s) { ... } — show loading only if it takes more than 100ms, then show for at least 1s. Impact: @defer enables granular code splitting at the template level without complex routing configuration.

Open this question on its own page
07

What is Angular CDK (Component Development Kit)?

The Angular CDK provides primitive building blocks for building custom, high-quality UI components without prescribing a specific design language (unlike Angular Material which follows Material Design). Key CDK packages: (1) @angular/cdk/overlay: creates and manages floating UI elements (tooltips, dropdowns, modals) with configurable positioning strategies (flexible, global, connected). Handles: scroll strategies, position recalculation, backdrop, keyboard navigation; (2) @angular/cdk/portal: render components or templates dynamically at arbitrary DOM locations; (3) @angular/cdk/drag-drop: drag and drop with reordering: cdkDrag, cdkDropList, cdkDropListGroup. Handles sorting, transferring between lists, animations; (4) @angular/cdk/virtual-scroll: efficiently render large lists: <cdk-virtual-scroll-viewport itemSize="50"><app-item *cdkVirtualFor="let item of items"></app-item></cdk-virtual-scroll-viewport>. Only renders visible items; (5) @angular/cdk/a11y: accessibility utilities — FocusTrap (trap focus in modal), FocusMonitor (track keyboard vs mouse focus), ListKeyManager (keyboard navigation in lists), AriaDescriber; (6) @angular/cdk/breakpoints: responsive layout utilities — BreakpointObserver watches viewport changes; (7) @angular/cdk/table: generic table that Angular Material table is built on; (8) @angular/cdk/stepper, tree, accordion — behavioral components without styling; (9) @angular/cdk/clipboard, platform, observers — utilities. The CDK is separate from Angular Material — you can use CDK primitives to build your own design system.

Open this question on its own page
08

What is Angular workspace and monorepo architecture?

An Angular workspace can contain multiple projects (apps and libraries) managed together, enabling monorepo architectures. Nx monorepo with Angular: Nx is the most popular tool for Angular monorepos, extending the Angular CLI with: project-aware caching (only rebuild changed projects), dependency graph visualization, affected commands (only test/lint/build projects changed since last commit), code generators for libraries, and module boundary enforcement. Angular workspace (built-in): one workspace can have multiple apps: ng new my-workspace --create-application=false; cd my-workspace; ng generate application admin-portal; ng generate application customer-portal;. And shared libraries: ng generate library shared-ui; ng generate library auth; Libraries are importable as if npm packages via TypeScript path aliases in tsconfig.json: "@myorg/shared-ui": ["dist/shared-ui"]. Library types: feature libraries (feature code for one app), UI libraries (shared components), data-access libraries (services, state management), utility libraries (pure functions, models). Enforcing boundaries: use ESLint import rules or Nx module boundary rules to prevent cross-domain imports (admin features can't import customer features). Publishing libraries: build with ng build shared-ui → produces dist/ with compiled code; publish to npm or use Nx Release for versioned publishing. Benefits: code sharing without npm publishing, atomic commits across projects, consistent tooling, single node_modules.

Open this question on its own page
09

What is Angular micro-frontend architecture?

Micro-frontends extend microservices principles to the frontend — splitting a large Angular application into independently developed, deployed, and released frontend pieces. Module Federation (Webpack 5): the most common Angular micro-frontend implementation. Each micro-app exposes its own Angular components/modules as remote entries. The shell app loads them at runtime. // webpack.config.js in remote app: new ModuleFederationPlugin({ name: "checkoutApp", filename: "remoteEntry.js", exposes: { "./CheckoutModule": "./src/app/checkout/checkout.module.ts" } }). Shell app: { path: "checkout", loadChildren: () => loadRemoteModule({ type: "module", remoteEntry: "https://checkout.example.com/remoteEntry.js", exposedModule: "./CheckoutModule" }).then(m => m.CheckoutModule) }. Nx with Module Federation: nx g @nx/angular:remote checkout --host=shell — generates the Module Federation configuration automatically. Challenges: (1) Shared dependencies (multiple Angular versions = large bundles); (2) Communication between micro-frontends (shared state, custom events, URL); (3) Routing coordination; (4) Style isolation (CSS-in-JS or strict naming); (5) Testing (integration tests span boundaries). Alternatives to Module Federation: iframes (maximum isolation, awkward UX), web components (framework-agnostic), single-spa (meta-framework, supports multiple frameworks). When to use: large org with many teams deploying independently; different technology stacks needed; regulatory boundaries between teams. Cost: high complexity — don't use unless the team/org size genuinely requires it.

Open this question on its own page
Back to All Topics 54 questions total