Angular Application Development Interview Questions

50 questions and answers covering Angular Application Development, from fundamentals through practical, debugging, and design-level topics.

50 of 50 interview questions

  1. What is an Angular component, and what three pieces typically make it up?beginnerAngular Fundamentals: Components, Templates & Directives

    A component is a TypeScript class decorated with `@Component`, paired with an HTML template and (usually) a CSS/SCSS stylesheet -- the class holds the component's logic and state, the template defines its view, and the decorator's metadata (selector, templateUrl, styleUrls) wires the three together.

  2. What does Angular's `{{ }}` interpolation syntax do, and what kind of expressions can it contain?beginnerAngular Fundamentals: Components, Templates & Directives

    Interpolation embeds a component property's value directly into the rendered template text (`<p>{{ user.name }}</p>`); it can contain simple property access and expressions, but is intentionally limited (no assignment, no multi-statement logic) to keep templates declarative and easy to reason about.

  3. What is property binding (`[property]="expression"`) and how does it differ from interpolation?intermediateAngular Fundamentals: Components, Templates & Directives

    Property binding sets a DOM element or component property directly from a component-class expression (`<img [src]="imageUrl">`), useful for non-string values or attributes that aren't purely textual; interpolation is really syntactic sugar over property binding for the common text-content case.

    <img [src]="user.avatarUrl" [alt]="user.name">
  4. What is event binding (`(event)="handler()"`) in an Angular template?beginnerAngular Fundamentals: Components, Templates & Directives

    It wires a DOM event (click, input, submit) to a method on the component class, so user interaction in the view invokes logic in the component (`<button (click)="save()">Save</button>`) -- the parentheses syntax distinguishes it visually from property binding's square brackets.

  5. What is the Angular CLI, and what are two common tasks it automates?beginnerAngular Fundamentals: Components, Templates & Directives

    The `ng` command-line tool that scaffolds and manages an Angular project -- commonly used to generate new components/services/modules with consistent boilerplate (`ng generate component`) and to run the dev server, build, lint, and test tasks (`ng serve`, `ng build`, `ng test`) with sensible project-wide defaults.

  6. What are Angular's built-in structural control-flow directives (or newer `@if`/`@for` syntax), and what do they do?intermediateAngular Fundamentals: Components, Templates & Directives

    Structural constructs conditionally include or repeat DOM content based on component data -- `*ngIf`/`@if` conditionally renders a block, `*ngFor`/`@for` repeats a block once per item in a collection -- the newer `@if`/`@for` block syntax (introduced in recent Angular versions) is now the recommended approach over the older `*ngIf`/`*ngFor` structural-directive syntax.

    @for (item of items; track item.id) {
      <li>{{ item.name }}</li>
    }
  7. Why does Angular's `@for` block require a `track` expression, and what happens if items are tracked incorrectly (e.g. by array index)?advancedAngular Fundamentals: Components, Templates & Directives

    `track` tells Angular how to identify which DOM nodes correspond to which data items across re-renders, so it can update/reorder/remove the minimum necessary DOM instead of re-rendering everything; tracking by array index instead of a stable identity (like an item's `id`) can cause Angular to reuse the wrong DOM node when the underlying list is reordered or filtered, leading to stale rendered state or lost component-level input focus.

    Common mistake: Tracking a @for loop by array index instead of a stable item id, causing Angular to misattribute DOM state (like focused inputs) when the list is reordered or filtered.

  8. What is an Angular attribute directive, and how does it differ from a structural directive?intermediateAngular Fundamentals: Components, Templates & Directives

    An attribute directive changes the appearance or behavior of an existing element without adding or removing elements from the DOM (e.g. `ngClass`/`ngStyle`, or a custom directive that changes an element's style on hover); a structural directive (`*ngIf`, `*ngFor`) actually adds, removes, or repeats elements in the DOM structure itself.

  9. What is Angular's component selector, and how is it used in a parent template?beginnerAngular Fundamentals: Components, Templates & Directives

    The `selector` metadata property (e.g. `selector: 'app-user-card'`) defines the custom HTML tag name a component is invoked by in another template (`<app-user-card></app-user-card>`) -- lets a component be reused anywhere in the application simply by referencing its tag name.

  10. Why is Angular described as a full, opinionated framework rather than just a library, relative to something like plain React?intermediateAngular Fundamentals: Components, Templates & Directives

    Angular ships with its own built-in solutions for routing, forms, HTTP client, dependency injection, and a mandated project structure/CLI, all designed to work together as a cohesive whole -- teams generally don't need to separately choose and wire together a router, state-management library, and HTTP client the way they often must in a leaner library-first approach.

  11. What does the `@Input()` decorator do, and how does data flow with it?beginnerInputs/Outputs, Change Detection & Dependency Injection

    It marks a component property as bindable from a parent template, letting a parent pass data DOWN into a child component (`<app-user-card [user]="selectedUser">`) -- data flows one direction, from parent to child, via `@Input()`.

  12. What does the `@Output()` decorator do, and how does it typically pair with an `EventEmitter`?intermediateInputs/Outputs, Change Detection & Dependency Injection

    It marks a component property (typically an `EventEmitter`) as an event a parent can listen to, letting a child component send data or notifications UP to its parent (`<app-user-card (userSelected)="onUserSelected($event)">`) -- `@Input()`/`@Output()` together form Angular's standard parent-child communication pattern.

    @Output() userSelected = new EventEmitter<User>();
    select(user: User) {
      this.userSelected.emit(user);
    }
  13. What is Angular's change detection, at a conceptual level?intermediateInputs/Outputs, Change Detection & Dependency Injection

    The mechanism Angular uses to detect when component data has changed and re-render the affected parts of the DOM to match -- triggered by events like user interaction, HTTP responses, or timers, Angular walks the component tree checking for changes and updates bindings accordingly.

  14. What is the difference between Angular's default change detection strategy and `OnPush`, and why might `OnPush` improve performance in a large app?advancedInputs/Outputs, Change Detection & Dependency Injection

    Default strategy checks a component (and potentially its whole subtree) on every change-detection cycle regardless of whether its actual inputs changed; `OnPush` tells Angular to only re-check a component when its `@Input()` REFERENCE changes (or an event originates from within it) -- in a large component tree, this can meaningfully reduce unnecessary re-render checks, but requires treating input data as immutable so reference changes reliably signal real updates.

  15. What is dependency injection (DI), and what problem does it solve for a component that needs a service?advancedInputs/Outputs, Change Detection & Dependency Injection

    DI is a pattern where a class declares the dependencies it needs (e.g. via constructor parameters) and an external system (Angular's injector) supplies (injects) those dependencies at runtime, rather than the class creating them itself -- this decouples a component from the concrete implementation details of its dependencies, making the component easier to test (by injecting a mock/fake) and the dependency easier to reuse/reconfigure app-wide.

  16. What does the `@Injectable()` decorator do, and what does `providedIn: 'root'` mean?intermediateInputs/Outputs, Change Detection & Dependency Injection

    `@Injectable()` marks a class as available for Angular's DI system to construct and inject; `providedIn: 'root'` registers it as a singleton available application-wide, meaning Angular creates exactly one shared instance for the whole app rather than a new instance per component that requests it.

    @Injectable({ providedIn: 'root' })
    export class UserService { }
  17. Why might a component receive different instances of the same injected service, depending on where that service is 'provided'?advancedInputs/Outputs, Change Detection & Dependency Injection

    Angular's DI is hierarchical -- a service can be provided at the root/application level (one shared instance), or at a specific component/module level (creating a new instance scoped just to that component and its children) -- registering a provider in a component's own `providers` array creates a fresh instance for that component's subtree, distinct from any root-level instance.

  18. How would you test a component that depends on an injected service, without hitting the service's real implementation (e.g. a real HTTP call)?advancedInputs/Outputs, Change Detection & Dependency Injection

    Use Angular's `TestBed` to configure a testing module where the real service is swapped out for a mock/stub via the `providers` array, so the component under test receives the fake implementation through the same DI mechanism it would use in production -- verifies the component's own logic without depending on the real service's actual behavior.

  19. What is two-way data binding via `[(ngModel)]`, and what two individual bindings does it combine?intermediateInputs/Outputs, Change Detection & Dependency Injection

    `[(ngModel)]` combines property binding (setting an input's value FROM a component property) and event binding (updating that component property WHEN the input changes) into a single, concise syntax -- commonly called 'banana in a box' for its `[( )]` shape -- primarily used for simple form input binding, with Angular's reactive forms generally preferred for more complex forms.

  20. What is a common mistake when mutating an `@Input()`-bound array or object directly, and how does it interact badly with `OnPush` change detection?advancedInputs/Outputs, Change Detection & Dependency Injection

    Mutating an array/object in place (e.g. `this.items.push(newItem)`) keeps the same object REFERENCE, so an `OnPush` child component watching that input for reference changes won't detect the update and won't re-render -- the fix is to create a new reference on change (e.g. `this.items = [...this.items, newItem]`), which reliably signals the change to `OnPush` components.

    Common mistake: Mutating an @Input()-bound array/object in place instead of creating a new reference, silently breaking change detection for any OnPush child relying on that input.

  21. What is an RxJS Observable, at a conceptual level, and how does it differ from a Promise?intermediateRxJS Observables & HttpClient

    An Observable represents a stream of values delivered over time (zero, one, or many), which can be subscribed to and later unsubscribed from; a Promise resolves exactly once and cannot be cancelled once started -- Observables are more general-purpose for ongoing streams (like user input events or WebSocket messages), while Promises fit a single async result.

  22. Why does Angular's `HttpClient` return an Observable rather than a Promise for HTTP requests?advancedRxJS Observables & HttpClient

    Returning an Observable lets a request be cancelled (unsubscribed) before it completes -- useful e.g. if a user navigates away or triggers a new search before the previous one resolves -- and lets RxJS operators (retry, debounce, combine with other streams) compose naturally with the request, which a plain Promise-based API wouldn't support as cleanly.

  23. Why is failing to unsubscribe from a manually-subscribed Observable considered a common source of memory leaks in Angular apps?advancedRxJS Observables & HttpClient

    A subscription that's never unsubscribed keeps its callback (and everything it references, including the component instance) alive even after the component is destroyed, since the Observable's producer still holds a reference to the subscriber -- over time, repeatedly navigating to and away from a component with leaked subscriptions can accumulate memory and continue firing logic against a component that should no longer be active.

    Common mistake: Manually subscribing to a long-lived Observable in ngOnInit without ever unsubscribing in ngOnDestroy, leaking the component and its subscription callback after the component is destroyed.

  24. What does Angular's `async` pipe do in a template (`{{ data$ | async }}`), and how does it help avoid subscription-management bugs?intermediateRxJS Observables & HttpClient

    The `async` pipe subscribes to an Observable (or Promise) directly in the template and automatically unsubscribes when the component is destroyed, and triggers change detection when a new value arrives -- eliminates the need to manually subscribe/unsubscribe in the component class, removing a common source of memory-leak bugs entirely.

    <div *ngIf="user$ | async as user">{{ user.name }}</div>
  25. What does the RxJS `map` operator do when applied to an HttpClient response Observable?intermediateRxJS Observables & HttpClient

    `map` transforms each emitted value through a function, producing a new Observable of transformed values -- e.g. `this.http.get<ApiUser[]>(url).pipe(map(users => users.map(u => u.name)))` transforms the raw API response into just a list of names, without needing a separate subscription/assignment step.

  26. What does the RxJS `switchMap` operator do, and why is it commonly used for a search-as-you-type feature backed by an HTTP call?advancedRxJS Observables & HttpClient

    `switchMap` maps each emitted value to a new inner Observable (like an HTTP request), automatically CANCELLING the previous inner Observable if a new outer value arrives before it completes -- for search-as-you-type, this ensures only the response for the MOST RECENT keystroke's request is used, discarding stale in-flight responses from earlier, now-outdated searches.

    searchTerm$.pipe(
      debounceTime(300),
      switchMap(term => this.http.get(`/api/search?q=${term}`))
    )
  27. What does `debounceTime` do in an RxJS pipeline, and why is it commonly paired with `switchMap` for search inputs?intermediateRxJS Observables & HttpClient

    `debounceTime(300)` waits for a pause of the given duration in emitted values before letting one through, suppressing rapid intermediate emissions -- paired with `switchMap` for a search box, it prevents firing an HTTP request on every single keystroke, instead waiting until the user briefly pauses typing.

  28. How would you handle an HTTP request error using RxJS's `catchError` operator, rather than letting it propagate unhandled to the subscriber?advancedRxJS Observables & HttpClient

    `catchError` intercepts an error emitted by the source Observable and lets you return a fallback Observable (e.g. an empty result or a default value) instead of letting the error terminate the stream unhandled -- commonly combined with logging the error for diagnostics before returning a graceful fallback.

    this.http.get(url).pipe(
      catchError(err => {
        console.error(err);
        return of([]);
      })
    )
  29. What is the difference between a 'cold' and a 'hot' Observable in RxJS?advancedRxJS Observables & HttpClient

    A cold Observable starts producing values fresh for EACH new subscriber (like a typical `HttpClient` request, which fires a new request per subscription); a hot Observable produces values independent of subscribers and shares the same stream across everyone currently subscribed (like a `Subject` broadcasting UI events) -- understanding this distinction matters for whether subscribing twice means 'run twice' or 'listen to the same ongoing stream.'

  30. How would you use RxJS's `forkJoin` (or `combineLatest`) to wait for multiple independent HTTP requests to complete before proceeding?advancedRxJS Observables & HttpClient

    `forkJoin({ user: this.http.get(...), orders: this.http.get(...) })` waits for ALL provided Observables to complete and then emits a single combined result once, useful for a screen that needs several independent pieces of data loaded before it can render meaningfully -- `combineLatest` is the related choice when you want the combined value updated every time ANY one of the source Observables emits again, not just once.

  31. What is the difference between Angular's template-driven forms and reactive forms?intermediateReactive Forms, Routing & Pipes

    Template-driven forms build the form model implicitly from directives in the template (using `ngModel`), suiting simple forms; reactive forms build the form model explicitly in the component class (using `FormGroup`/`FormControl`), giving more direct, testable, and predictable control over validation and value changes -- generally the recommended approach for anything beyond a trivial form.

  32. What are `FormGroup` and `FormControl` in Angular's reactive forms, and how do they relate to each other?intermediateReactive Forms, Routing & Pipes

    `FormControl` represents a single form field's value and validation state; `FormGroup` aggregates multiple `FormControl`s (or nested `FormGroup`s) into a single form model, exposing the combined validity and value of the whole group -- this composable structure lets a complex, nested form be modeled as a tree of controls and groups.

    form = new FormGroup({
      name: new FormControl('', Validators.required),
      email: new FormControl('', [Validators.required, Validators.email]),
    });
  33. How do Angular's built-in validators (like `Validators.required`) work, and how would you display a validation error in the template?intermediateReactive Forms, Routing & Pipes

    Validators are attached to a `FormControl` and update its status (`valid`/`invalid`) and `errors` object as the value changes; a template checks `form.get('email')?.hasError('required')` (typically combined with a `touched`/`dirty` check, so errors don't show before the user has interacted with the field) to conditionally render an error message.

  34. What is a custom validator function in Angular's reactive forms, and what shape must it have?advancedReactive Forms, Routing & Pipes

    A function taking an `AbstractControl` and returning either `null` (valid) or a `ValidationErrors` object (invalid, with a key describing the failure) -- lets you express business-specific validation logic (like 'password must contain a digit') beyond Angular's built-in validators, attached to a control the same way built-in validators are.

  35. What is the Angular Router, and what does a route configuration typically map?beginnerReactive Forms, Routing & Pipes

    The Router maps URL paths to components, letting an Angular single-page application render different views without a full page reload -- a route configuration is an array of objects each pairing a `path` string with a `component` (or lazy-loaded module/component) to render when that path matches.

    const routes: Routes = [
      { path: 'users/:id', component: UserDetailComponent },
    ];
  36. What is a route parameter (`:id` in `users/:id`), and how would a component read its value?intermediateReactive Forms, Routing & Pipes

    A route parameter is a dynamic segment of a URL path captured as a named value -- a component reads it by injecting `ActivatedRoute` and subscribing to `route.paramMap` (or reading a snapshot for a component that won't be reused across parameter changes), giving access to the matched value (e.g. the specific user ID) for that navigation.

  37. What is a route guard in Angular, and give an example of when you'd use one?advancedReactive Forms, Routing & Pipes

    A route guard is a function that runs before a route is activated (or deactivated) and can allow, block, or redirect the navigation -- a common example is an authentication guard (`CanActivate`) that checks whether a user is logged in and redirects to a login page if not, preventing access to a protected route.

  38. What is an Angular pipe, and what does the built-in `date` pipe do as an example?beginnerReactive Forms, Routing & Pipes

    A pipe transforms a value for display directly in a template using `|` syntax, without altering the underlying component data -- `{{ order.createdAt | date:'mediumDate' }}` formats a raw Date/timestamp into a human-readable date string, keeping formatting logic out of the component class.

  39. What is the difference between a 'pure' and an 'impure' pipe in Angular, and why does purity matter for performance?advancedReactive Forms, Routing & Pipes

    A pure pipe (the default) only re-executes when its input reference changes, which Angular can check cheaply; an impure pipe re-executes on EVERY change-detection cycle regardless of whether the input actually changed, which is far more expensive -- impure pipes should be used sparingly and only when genuinely needed (e.g. transforming a mutated-in-place array), since overusing them can noticeably hurt performance in a large app.

  40. What is lazy loading in the context of Angular routing, and why does it matter for a large application's initial load time?advancedReactive Forms, Routing & Pipes

    Lazy loading configures a route to load its component/module code only when that route is actually navigated to, rather than bundling it into the app's initial JavaScript payload -- for a large application with many feature areas, this can significantly reduce the amount of code a user must download before the app becomes interactive, since features they never visit are never downloaded at all.

  41. What is Angular's standalone component API, and how does it change (or remove the need for) the older `NgModule`-based project structure?advancedArchitecture, Performance & Testing

    Standalone components declare their own dependencies (imports) directly rather than being declared inside an `NgModule`, letting a project be structured around components/directives/pipes without the older mandatory `NgModule` wiring -- newer Angular applications increasingly default to a standalone-first structure, though `NgModule`s remain supported for existing codebases and specific use cases.

  42. How would you decide whether Angular's full, opinionated framework is a good fit for a given project, versus a leaner library-first approach?advancedArchitecture, Performance & Testing

    Angular's built-in DI, routing, forms, and enforced project structure tend to pay off most on larger, longer-lived applications built by bigger teams, where consistency and built-in conventions reduce architectural decision fatigue; for a small, short-lived project, or a small team wanting maximum flexibility over their own tooling choices, a leaner library-first approach may add less upfront overhead.

  43. What is a 'smart' (container) versus 'dumb' (presentational) component pattern in Angular, and what does separating them help with?advancedArchitecture, Performance & Testing

    A smart/container component manages state and business logic (often injecting services, subscribing to data), while a dumb/presentational component receives data purely via `@Input()`/`@Output()` and focuses only on rendering -- separating the two makes presentational components easier to reuse and test in isolation, since they have no dependency on services or app-wide state.

  44. How would you write a unit test for an Angular component using `TestBed` and `ComponentFixture`?advancedArchitecture, Performance & Testing

    `TestBed.configureTestingModule` sets up a testing module declaring/importing the component (and any dependencies, often mocked); `TestBed.createComponent` returns a `ComponentFixture` giving access to the component instance and its rendered DOM (`fixture.nativeElement`), letting a test assert on both the component's internal state and what it actually renders after calling `fixture.detectChanges()`.

  45. Why is calling `fixture.detectChanges()` necessary in an Angular component test after changing a component property?advancedArchitecture, Performance & Testing

    Angular's change detection doesn't run automatically inside a test the way it does during real app execution -- `fixture.detectChanges()` explicitly triggers a change-detection cycle, updating the rendered DOM to reflect the component's current state, so an assertion checking the DOM after a property change needs this call first or it will see stale, pre-update markup.

    Common mistake: Asserting on rendered DOM content immediately after changing a component property without calling fixture.detectChanges() first, and getting a false failure from stale markup.

  46. What is Ahead-of-Time (AOT) compilation in Angular, and how does it differ from Just-in-Time (JIT) compilation?advancedArchitecture, Performance & Testing

    AOT compiles Angular templates into efficient JavaScript at BUILD time (before the app is shipped to the browser), producing smaller bundles and faster startup since no compiler needs to ship to (or run in) the browser; JIT compiles templates in the browser at RUNTIME -- AOT is the default and recommended approach for production builds.

  47. What is `trackBy` (or the `track` expression in the newer `@for` syntax) used for beyond correctness -- what performance problem does it solve?advancedArchitecture, Performance & Testing

    Without a stable tracking identity, Angular may treat a re-rendered list as entirely new items on each update, destroying and recreating every DOM node even if most items are unchanged -- providing a stable `track`/`trackBy` identity lets Angular recognize which items persisted, only updating/moving/removing the DOM nodes that actually need it, which is significantly cheaper for large or frequently-updated lists.

  48. How would you debug an Angular component that appears to not be receiving an updated `@Input()` value from its parent?advancedArchitecture, Performance & Testing

    Check whether the parent is passing a NEW reference for reference/object-type inputs (especially if the child uses `OnPush`), verify the binding syntax in the parent template is actually correct (a typo'd property name binds silently to nothing rather than erroring in some cases), and use Angular DevTools or a temporary log inside `ngOnChanges` to confirm whether and when the input actually changes from the child's perspective.

  49. What is the `ngOnChanges` lifecycle hook, and what does its `SimpleChanges` parameter provide?advancedArchitecture, Performance & Testing

    `ngOnChanges` runs whenever one or more `@Input()`-bound properties change (before the updated values are rendered), receiving a `SimpleChanges` object describing each changed input's previous and current value -- useful for reacting to a specific input's change (e.g. re-fetching data) rather than relying only on the updated property value itself.

  50. Why is 'when Angular fits' considered a genuine architectural decision rather than a purely technical one, and what factors typically drive it?advancedArchitecture, Performance & Testing

    The choice trades off team size/experience (Angular's conventions can onboard new team members faster on a large existing codebase), project longevity (upfront structure pays off more over a multi-year lifespan), and how much the team values built-in, opinionated tooling versus assembling their own stack -- there's no universally 'correct' framework choice, only a better or worse fit for a specific project's constraints and team.