Skip to content

Recap

Angular — Interview Revision

Lifecycle Hooks (in order)

  1. constructor — DI only, no inputs yet
  2. ngOnChanges — fires when @Input changes, before init
  3. ngOnInit — inputs available, do setup here
  4. ngOnDestroy — clean up subscriptions

constructor vs ngOnInit — constructor for DI only, ngOnInit for logic. Inputs not available in constructor.


Change Detection

Default — Zone.js triggers full component tree check on every async event (click, HTTP, setTimeout, Promise).

OnPush — only re-checks component if:

  • @Input reference changes (not mutation — new object/array)
  • Event fires inside component
  • Observable emits via async pipe
  • markForCheck() called manually
@Component({
  changeDetection: ChangeDetectionStrategy.OnPush
})

Key rule — OnPush compares by reference. Mutating an array doesn't trigger it. Spread to create new reference:

this.items = [...this.items, newItem]; // triggers
this.items.push(newItem); // doesn't trigger

Smart vs Dumb Components

Dumb (Presentational):

  • @Input for data in
  • @Output for events out
  • No services, no API calls
  • Reusable, easily testable

Smart (Container):

  • Injects services
  • Makes API calls
  • Manages state
  • Passes data down, handles events up

Subscriptions — When to Unsubscribe

HTTP observables — complete automatically after one response. No unsubscribe needed.

Subjects, timers, WebSockets — never complete. Must unsubscribe.

Four ways:

// 1. Manual
ngOnDestroy() { this.sub.unsubscribe(); }

// 2. takeUntil
private destroy$ = new Subject();
this.data$.pipe(takeUntil(this.destroy$)).subscribe(...);
ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); }

// 3. async pipe — Angular handles it
{{ data$ | async }}

// 4. takeUntilDestroyed (Angular 16+)
this.data$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(...);

RxJS Operators

Scenario Operator
Search input, cancel previous switchMap
Form submit, ignore while busy exhaustMap
Upload queue, one at a time in order concatMap
Independent parallel calls mergeMap

setInterval vs setTimeout

  • setTimeout(fn, 3000) — fires once after 3 seconds
  • setInterval(fn, 3000) — fires every 3 seconds regardless

Problem with async inside setInterval: setInterval doesn't await the callback. Calls pile up, responses arrive out of order, stale data overwrites fresh.

Fix — recursive setTimeout:

async function sync() {
    await fetch('/api/sync');
    setTimeout(sync, 3000); // next only after previous completes
}

Angular fix:

timer(0, 3000).pipe(
    switchMap(() => this.http.get('/api/sync')),
    takeUntilDestroyed()
).subscribe(...);

DI in Angular

  • RootprovidedIn: 'root', one instance app-wide
  • Componentproviders: [MyService] in component, one instance per component
  • Moduleproviders in @NgModule, one instance per module (legacy)

Two-way vs One-way Binding

  • [property] — one-way, parent to child
  • (event) — one-way, child to parent
  • [(ngModel)] — two-way, banana in a box