29

Currently I'm switching from http (@angular/http) to HttpClient (@angular/common/http) and have problems mapping my response to objects.

Old code (was working before)

this.http.get(environment.baseUrl + '/api/timeslots')
            .map((response: Response) => {
                    const data = response.json();
                    const timeslots = Array.of<Timeslot>();
                    for (const item of data) {...}

New code, but compilation error:

this.httpClient.get(environment.baseUrl + '/api/timeslots')
                .map((response: Response) => {
                        const data = <Timeslot[]> response;
const timeslots = Array.of<Timeslot>();
                    for (const item of data) {...}

Do I miss a cast? The response is an array of Timeslots.

netshark1000
  • 7,245
  • 9
  • 59
  • 116

4 Answers4

36

Default value that returns new HttpClient is Object. It automatically calls response.json() internally.

You can tell HttpClient what type the response will be, so:

this.httpClient.get<Timeslot[]>(...)
 .map((timeSlots) => {
   ...

where timeSlots's type will be Timeslot[]

See more information about typecheking in new HttpClient

yurzui
  • 205,937
  • 32
  • 433
  • 399
  • Note that the data is not actually validated. The data returned can actually be any object. Not the Timeslot you would expect. – Nux Jan 11 '21 at 04:31
  • Also note that this not only doesn't call the constructor of Timeslot. It doesn't even enforce types. So if you have Timeslot with `userId: string` and the server returns a number, then you will get a number in JS. – Nux Jan 11 '21 at 04:53
34

Since Angular 6/7 there have been a couple of modifications to the implementation of the RxJS library within Angular.

Hence, the RxJS operators are now located at 'rxjs/operators' instead of 'rxjs/add/operator/:OperatorName'. Also, these operators can no longer be directly chained to the observebale, instead they have to be chained through the pipe() method.

So, the new way of implementing this must look like this:

import {map} from 'rxjs/operators';

this.http.get(...)
  .pipe(
    map( response => {
     // TODO: Do Your Staff Here! 
   } )
  );

Please, refer to the specific Angular's documentation here for more details. * Here I'm assuming that your HttpClient component is available under this.http prop.

mytuny
  • 845
  • 9
  • 15
13

You still can, but you need to import the map()-operator from rxjs like this:

import 'rxjs/add/operator/map';

And you will have the map() operator.

(See also my answer here: https://stackoverflow.com/a/48323851/348841)

Jonas Stensved
  • 14,378
  • 5
  • 51
  • 80
0

I will leave here the code that works for me to map response to what I need.

I have a dummy response that looks like this.

[
    {
        "helloId": 52,
        "hello1": "NiceTag",
        "hello2": "time",
        "tag": {
            "type": "NICE_HELLO",
            "name": "9f7938bd-3d98-4531-a5df-1c503aed6a74"
        }
    }
]

Here is the way I map it to an object

 getHello(id: string) {
    return this.http.get<any>(`http://localhost:8080/hello/${id}`)
      .pipe(map(response => ({
          hello: response[0].hello1
      })));
  }

Note the following things:

  • My response is a list, so I needed to map values of the first element -response[0]
  • To be able to refer to the fields directly (meaning the way they are written in the response) like response[0].hello1 you need to add <any> after get. Otherwise you will get an Error sth like Property 'hello1' does not exist on type 'Object'
  • In my example there is no subscribe cause I am using it later in another function, but it can look sth like this, where I already refer to the value of the field by the name I gave it to while mapping.
.subscribe(data => {
      this.getResponse = data.hello;
})

And yes you of course need to import map like so import {map} from "rxjs";

I use Angular 13, btw

Diana
  • 935
  • 10
  • 31