In Angular, how do the Observables from HttpClient, Router.events, ActivatedRoute.paramMap and a form control's valueChanges differ in emissions and completion?
answer
- one response versus a stream
- cold per subscriber or shared
- current value on subscribe
- no initial value
- which ones ever complete
basics
~20 sAn HttpClient call is cold: each subscription sends a request, emits one response body and completes. Router.events, ActivatedRoute.paramMap and valueChanges are shared, long-lived streams that never complete; paramMap replays the current params on subscribe, while the other two only emit future events.
solid answer
~40 s`HttpClient.get()` returns a **cold** Observable: nothing happens until you subscribe, each subscription sends its own request, and with the default `observe: 'body'` it emits one value and completes (or errors). `Router.events` is a **hot**, shared stream of every router event; it has no current value, so a late subscriber only sees future events, and it never completes during the app's life. `ActivatedRoute.paramMap` is backed by a `BehaviorSubject`, so it emits the current params **synchronously** on subscribe, emits again only when params change while the component is reused, and never completes. `valueChanges` on a form control is an `EventEmitter`: hot, no initial value, one emission per change, never completing. The never-completing three need a deliberate teardown when you subscribe to them manually.
go deeper
Recall that HTTP calls emit once and finish, while router and form streams keep emitting and never finish on their own.
Explain for each API what backs it, whether it emits on subscribe, and whether it completes.
Use these profiles to review components for leaked router or form subscriptions and for accidental duplicate requests.
Set team conventions for consuming framework streams, such as preferring the async pipe or toSignal over manual subscriptions.
## Why the differences matter Angular hands you Observables from several of its own APIs, and they look identical in type signatures: `Observable<T>`. Their behaviour is not identical. Knowing, for each one, **when it first emits**, **how many values it emits** and **whether it ever completes** tells you whether you will miss a value, fire a request twice, or keep a subscription alive after the component that created it is gone. This answer assumes Angular 22.2 and RxJS 7.8, and uses a product-detail page as the running example. ## The four sources side by side | API | What backs it | First emission | Emissions | Completes? | | --- | --- | --- | --- | --- | | `HttpClient.get()` (default `observe: 'body'`) | A new request per subscription | When the response arrives | One | Yes, after the response, or it errors | | `Router.events` | A `Subject` inside the root `Router` | The next router event after you subscribe | Many | No; the router only unsubscribes its observers when it is disposed | | `ActivatedRoute.paramMap` | A `BehaviorSubject` of params, mapped to a `ParamMap` | Immediately, synchronously, with the current params | One more per param change while the component is reused | No | | `FormControl.valueChanges` | An `EventEmitter` created by the control | The next value change | One per change | No | ## Reading the table - **Cold versus shared.** `HttpClient` builds its Observable as `of(request)` followed by the interceptor chain and backend, so every subscription re-runs interceptors and sends a new request. The other three are single shared instances: every subscriber sees the same emissions, and subscribing does not trigger any work. - **Current value versus future only.** `paramMap` is the only one that answers "what is it now?" at subscription time, because its source is a `BehaviorSubject`. `Router.events` and `valueChanges` have no memory: subscribe after a navigation or keystroke and you do not see it. - **Completion.** Only the HTTP call finishes on its own. The router, the activated route and the form control live as long as the application, the routed component or the form, and their streams stay open for all of that time. ## A product-detail page, concretely ```ts export class ProductDetail { private readonly http = inject(HttpClient); private readonly route = inject(ActivatedRoute); private readonly router = inject(Router); readonly quantity = new FormControl(1); constructor() { this.route.paramMap.subscribe((p) => console.log('id now', p.get('id'))); // fires at once this.router.events.subscribe((e) => console.log('router event', e)); // future events only this.quantity.valueChanges.subscribe((q) => console.log('qty', q)); // no initial 1 this.http.get('/api/products/42').subscribe((p) => console.log(p)); // one value, completes } } ``` The first subscription logs immediately. The router and quantity subscriptions log nothing until something happens. The HTTP subscription logs once and is finished. ## Consequences for teardown 1. The HTTP subscription cannot outlive its response, so it does not leak by staying open. Unsubscribing early is still meaningful: it **cancels** the in-flight request. 2. The three never-completing streams keep calling your callback for as long as their source lives. `Router.events` in particular belongs to a root singleton, so a subscription created in a component keeps running after that component is destroyed unless you end it. 3. The async pipe, `toSignal()` and `takeUntilDestroyed()` exist to end such subscriptions with the view or injector; how each works is a separate topic. ## Common confusions - "`paramMap` only emits on navigation." It also emits the current value synchronously on subscribe. - "`valueChanges` starts with the initial value." It does not; you add that yourself if you need it. - "HTTP Observables are hot, so two subscribers share one request." Each subscription sends its own request.
- Why does a subscription to Router.events in a component keep running after the component is destroyed?`Router.events` belongs to the root `Router`, which lives for the whole application and never completes the stream. Destroying the component does not touch the router, so the subscription and its callback stay active until you unsubscribe.
- What changes if you call HttpClient.get() with observe: 'events' and reportProgress: true?The Observable emits several `HttpEvent` values: a sent event first, then header and progress events while the response downloads, and finally the `HttpResponse`, then completes. It is still cold: each subscription sends its own request.
- Does paramMap emit again when the user navigates to the same URL?Not for unchanged params. The router pushes new params into the route's subject only when they differ from the previous ones by shallow equality, so re-navigating to `/products/42` from `/products/42` does not re-emit.
An HttpClient call is a phone call that ends when you get the answer; Router.events and valueChanges are radio stations you tune into mid-broadcast; paramMap is a noticeboard that shows today's notice the moment you look and a new one whenever it changes.
saying these in an interview costs you the question
- Every Angular Observable completes when its component is destroyed.
- HttpClient Observables need an unsubscribe to avoid a memory leak.
- valueChanges emits the control's current value when you subscribe.
- Router.events replays the last navigation to new subscribers.
- Two subscriptions to one http.get() share a single request.