Question
In an Angular 2 application, I want to fetch a JSON list through an HTTP service and display it in a component. The following service calls http.get(...).map(...), but it throws this error:
TypeError: this.http.get(...).map is not a function
How can I make the RxJS map operator available and correctly return parsed JSON from the HTTP request?
import { Injectable } from "angular2/core";
import { Http, Response } from "angular2/http";
import { Hall } from "./hall";
@Injectable()
export class HallService {
public static PATH: string = "app/backend/";
constructor(private http: Http) {}
getHalls() {
return this.http
.get(HallService.PATH + "hall.json")
.map((res: Response) => res.json());
}
}
The component subscribes to the returned value:
ngOnInit() {
this._service.getHalls().subscribe((halls: Hall[]) => {
this.halls = halls;
});
}
Short Answer
You will learn why an Angular HTTP request returns an RxJS Observable, why .map() may be missing at runtime, and how to import and use the operator correctly in legacy Angular 2 applications. You will also see the equivalent approach for modern Angular.
Concept
Angular 2's legacy Http service returns an RxJS Observable from http.get().
An Observable represents a value that will arrive later, such as an HTTP response. Its value is not available immediately, so you use operators to transform it and subscribe() to receive it.
In older RxJS versions, operators such as map were not always attached to Observable automatically. The TypeScript code can compile, but the browser may still fail at runtime if the module that adds map was never loaded:
.map is not a function
For the old Angular 2 Http API, import the operator for its side effect:
import "rxjs/add/operator/map";
That import extends the Observable prototype with .map(). Then res.json() transforms the legacy Angular Response object into the JavaScript value represented by the JSON response.
This matters because imports affect both:
- Type information: what TypeScript believes is available.
- Runtime code: what methods actually exist in the browser.
Mental Model
Think of an Observable as a conveyor belt that will deliver a package later.
http.get()starts a conveyor belt that will deliver an HTTPResponse.map()is a processing station that opens the package and converts its contents withres.json().subscribe()is the receiving desk where your component gets the processed result.
In legacy RxJS, the map processing station is not installed unless you import it. Without that import, calling .map() is like asking for a machine that does not exist on the conveyor belt.
Syntax and Examples
For Angular 2's legacy Http module, import map before calling the instance method.
import { Injectable } from "angular2/core";
import { Http, Response } from "angular2/http";
import "rxjs/add/operator/map";
import { Observable } from "rxjs/Observable";
import { Hall } from "./hall";
@Injectable()
export class HallService {
private static readonly PATH = "app/backend/";
constructor(private http: Http) {}
getHalls(): Observable<Hall[]> {
return this.http
.(. + )
.( response.() []);
}
}
Step by Step Execution
Consider this legacy Angular 2 code:
return this.http
.get("app/backend/hall.json")
.map((response: Response) => response.json());
Execution proceeds as follows:
getHalls()is called by the component.this.http.get(...)creates and returns an Observable. At this point, the response has not necessarily arrived..map(...)registers a transformation: when aResponsearrives, callresponse.json().getHalls()returns the transformed Observable to the component.- The component calls
.subscribe(...). - Subscribing starts the HTTP request for this cold Observable.
- When the server returns
hall.json, the HTTP Observable emits aResponse. map()converts thatResponseto a JavaScript array or object.
Real World Use Cases
Observable transformations are useful whenever an HTTP response must be converted before the UI uses it.
- API response parsing: Convert a response body into
Hall[],User[], or a singleOrder. - Data shaping: Convert server field names or nested structures into the shape required by a component.
- Filtering data: Remove inactive records before displaying a list.
- Calculating view values: Convert prices, dates, or status codes into display-ready values.
- Combining requests: In modern RxJS, compose related API requests before updating a screen.
Example transformation in legacy Angular 2:
return this.http
.get("app/backend/hall.json")
.map((response: Response) => response.json() as Hall[])
.map((halls: Hall[]) => halls.filter(hall => hall.isOpen));
Real Codebase Usage
In production code, services usually expose typed Observables and keep HTTP details out of components.
Keep parsing in the service
getHalls(): Observable<Hall[]> {
return this.http
.get("app/backend/hall.json")
.map((response: Response) => response.json() as Hall[]);
}
The component only handles UI state:
ngOnInit(): void {
this._service.getHalls().subscribe(
halls => this.halls = halls,
error => this.errorMessage = "Could not load halls."
);
}
Validate data at application boundaries
A TypeScript assertion such as as Hall[] helps the compiler, but it does validate server data at runtime. Real applications often validate important API data, especially when data comes from external services.
Common Mistakes
Forgetting to import the legacy operator
This causes the error in the question:
// Broken in legacy RxJS if map was not loaded
return this.http.get("app/backend/hall.json").map(res => res.json());
Fix it with:
import "rxjs/add/operator/map";
Importing map using modern RxJS syntax in an old Angular 2 project
This is for modern RxJS and does not add a .map() instance method:
import { map } from "rxjs/operators";
For the legacy chained style, use rxjs/add/operator/map. For current Angular, use pipe(map(...)).
Using Http without registering HTTP providers
In older Angular 2 setups, Http must be configured in application bootstrap providers. Depending on the Angular 2 version, this commonly used or .
Comparisons
| Situation | Legacy Angular 2 Http | Modern Angular HttpClient |
|---|---|---|
| Import path | angular2/http | @angular/common/http |
| Value emitted by GET | Response | Parsed response body by default |
| Parse JSON | response.json() | Usually unnecessary |
| Transform syntax | .map(...) after importing an RxJS patch | .pipe(map(...)) |
| Operator import | import "rxjs/add/operator/map"; |
Cheat Sheet
// Legacy Angular 2 Http: enable the chained map operator
import "rxjs/add/operator/map";
// Legacy Angular 2 Http: parse the Response body
getHalls(): Observable<Hall[]> {
return this.http
.get("app/backend/hall.json")
.map((response: Response) => response.json() as Hall[]);
}
// Component: receive the result
this.service.getHalls().subscribe(
halls => this.halls = halls,
error => console.error(error)
);
// Modern Angular HttpClient: JSON is already parsed
getHalls(): Observable<Hall[]> {
..<[]>();
}
FAQ
Why does http.get(...).map say it is not a function?
In legacy Angular 2/RxJS, the Observable returned by Http.get() does not receive the .map() method until you load it with import "rxjs/add/operator/map";.
Where should I import rxjs/add/operator/map?
Import it in the service file that calls .map(), or in a shared RxJS setup file loaded before that service. Importing it directly in the service is easiest to understand in a small project.
Does map() parse JSON automatically?
No. In the old Http API, map() runs your function. response.json() inside that function parses the response body.
Should I use angular2/http in a new project?
No. It is a legacy Angular 2 API. Use HttpClient from @angular/common/http in modern Angular applications.
Why does modern Angular use pipe(map(...)) instead of .map(...)?
Mini Project
Description
Build a small legacy Angular 2 data service that loads a list of halls from a JSON file. The project demonstrates the missing piece behind the runtime error: loading the RxJS map operator before using chained Observable methods.
Goal
Fetch hall.json, transform the legacy HTTP response into a Hall[], and display the hall names after subscribing.
Requirements
Requirement list is provided below.
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Accessing Input Value from EventTarget in TypeScript
Learn why EventTarget has no value property in TypeScript and safely read values from HTML input events in Angular applications.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.