4

I have a working code using Http and I would like to learn how to convert this to use the latest HttpClient.

I have done the following steps:

  • in App.module.ts: import { HttpClientModule } from "@angular/common/http";
  • add HttpClientModule into the imports array
  • in the below service file (see below): import { HttpClient } from "@angular/common/http";
  • Inject HttpClient instead of Http in the constructor (replace Http with HttpClient.

Now, the following steps I can't get correct (how to rewrite return this.http.get(queryUrl)..)

Any ideas?

...

Code working using Http:

import { Injectable, Inject } from "@angular/core";
import { Http, Response } from "@angular/http";
import { Observable } from "rxjs/Observable";
import { SearchResult } from "../models/search-results.model";

export const YOUTUBE_API_KEY = "AIzaSyCIYsOjnkrIjZhVCFwqNJxe1QswhsliciQ";
export const YOUTUBE_API_URL = "https://www.googleapis.com/youtube/v3/search";

@Injectable()
export class YoutubeSearchService {
  constructor(
    private http: Http,
    @Inject(YOUTUBE_API_KEY) private apiKey: string,
    @Inject(YOUTUBE_API_URL) private apiUrl: string,
  ) {}

  search(query: string): Observable<SearchResult[]> {
    const params: string = [
      `q=${query}`,
      `key=${this.apiKey}`,
      `part=snippet`,
      `type=video`,
      `maxResults=10`,
    ].join("&");
    const queryUrl = `${this.apiUrl}?${params}`;

    return this.http.get(queryUrl).map((response: Response) => {
      return (<any>response.json()).items.map(item => {
        // console.log("raw item", item); // uncomment if you want to debug
        return new SearchResult({
          id: item.id.videoId,
          title: item.snippet.title,
          description: item.snippet.description,
          thumbnailUrl: item.snippet.thumbnails.high.url,
        });
      });
    });
  }
}

Here is the updated code

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
Isak La Fleur
  • 4,428
  • 7
  • 34
  • 50
  • The new HttpClient automatically assumes you are returning json, so there is no need for `response.json()` anymore. I would recommend reading the docs (https://angular.io/guide/http). It should have everything you need. – LLai Nov 10 '17 at 20:33
  • what error you are getting or what is the problem you face? – Aravind Nov 10 '17 at 20:38

2 Answers2

7

Response types are now controlled by responseType option, which defaults to json. This eliminates .map((response: Response) => response.json()) line.

HttpClient methods are generic, and response object type defaults to Object. The type can be provided for stronger typing:

return this.httpClient.get(queryUrl).map(response => {
  return response.items.map(
    item =>
      new SearchResult({
        id: item.id.videoId,
        title: item.snippet.title,
        description: item.snippet.description,
        thumbnailUrl: item.snippet.thumbnails.high.url,
      }),
  );
});

It is beneficial to rename service instance to httpClient to avoid confusion with Http instances.

After when you want to use the service, you subscribe to it.

Lets say youtube is the injected YoutubeSearchService in the component.

  queryYoutube() {
    this.youtube.search("Rio de Janeiro").subscribe(data => console.log(data));
  }
Isak La Fleur
  • 4,428
  • 7
  • 34
  • 50
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • Hi @estus, thank you for your answer! I created a fiddle (https://jsfiddle.net/u00pvkvs/1/) for the changed code, please see here: I followed your suggestion, please see here: // Property 'items' does not exist on type 'Object'. – Isak La Fleur Nov 10 '17 at 21:27
  • Here is a link of a json response https://www.googleapis.com/youtube/v3/search?q=Rio%20de%20Janeiro&key=AIzaSyCIYsOjnkrIjZhVCFwqNJxe1QswhsliciQ&part=snippet&type=video&maxResults=10 – Isak La Fleur Nov 10 '17 at 21:30
  • 2
    That's what the answer explains. Consider providing a proper type like ISearchResultItems . You can use `any` of course but this will elminte type safety. – Estus Flask Nov 10 '17 at 21:50
1

In angular 5 you can use the .map function inside the .pipe twice to do the same thing as in the previous angular version:

getCoinRankings () : Observable<Coinobject[]>{
  return this.http.get<Coinobject[]>(this.coinmarketcapAPI)
    .pipe(
      map(x => x.map(data => new Coinobject(data))),
      tap(coinrankings => console.log(`fetched coinrankings`)),
      catchError(this.handleError('getCoinrankings',[]))
    )
}

x = the request.json() -> the whole json response data = the individual item in the json array. In my example I used a constructor that would manually map the json to the required properties, but you can also do it with like this.

return this.httpClient.get<Object[]>(queryUrl).pipe(
    map(response => response.map(data => new SearchResult{
        id: item.id.videoId,
        title: item.snippet.title,
        description: item.snippet.description,
        thumbnailUrl: item.snippet.thumbnails.high.url,
      })),
    tap(...),
    catchError(...)
    )
}

Not that the user of a decrepated module would hurt your application offcourse... (but feel free to correct me)

obiwankoban
  • 143
  • 2
  • 6