Haidepzai
Haidepzai

Reputation: 1060

Chaining HTTP call in Angular

I'm trying to get Google Places with their API and get a response body that contains a "photo_reference" property. With this property, I want to get the Google Place Image and call a second API.

I have an input field that can search for places. By entering a string and clicking on the search button, this method will get called:

@Component({
  selector: 'app-search',
  templateUrl: './search.component.html',
  styleUrls: ['./search.component.css']
})
export class SearchComponent implements OnInit {
  private lng: number;
  private lat: number;
  private place_id: string;
  private listOfPoi: GoogleLocation[];

  constructor(
    private gs: GoogleService,
    private ds: DataService,
    private router: Router
    ) { }

  ngOnInit(): void {
    this.ds.getLocation().subscribe(loc => this.listOfPoi = loc);
  }

searchPoi(location: string): void {
    this.router.navigate(['/poi']);    
    this.gs.getPoi(location).pipe(
      map((response) => response)
    ).subscribe((data: GoogleResponse) => {
      if(data.status === "OK") {
        this.ds.setLocation(data.results); //data.results is an array of locations
      }      
    });
  }

In my other component, I try to get the locations and map them into a new object with the property imgUrl. I want to save them in an array.

export class SightseeingViewComponent implements OnInit {
  listOfPoi: LocationWithImage[];

  constructor(private ds: DataService, private gs: GoogleService) {}

  ngOnInit(): void {
    this.ds.getLocation().subscribe(
      (loc) =>
        (this.listOfPoi = loc.map((loc) => {
          return {
            ...loc,
            imgUrl:            
            this.gs.getImage(loc.photos[0].photo_reference).toString(),
          };
        }))
    );
  }
}

But my problem is the img src="[object Object]" it should be the string!

My problem is this line this.gs.getImage(loc.photos[0].photo_reference).toString() I tried to replace this line with a random hardcoded img Url and it works. The image are shown but I cannot retrieve the URL as string with this method. Of course toString() is not working but I have no clue what else I can do?

This is the google location model:

//The model of a google location object from the API response (getLocationInfo)
export class GoogleResponse {
    constructor(
        public results: GoogleLocation[],
        public status: string
        ) {}

}

export class GoogleLocation {
    constructor(
        public formatted_address: string,
        public geometry: Locations,
        public icon: string,
        public name: string,
        public photos: PhotoInfo[],
        public place_id: string,
        public reference: string,
        public type: string[]
    ) {}
}

export interface LocationWithImage extends GoogleLocation {
    imgUrl?: string;
}
...

EDIT:

This is my service: With this, I make a call to my backend which is fetching the photo_reference from Google via its API

@Injectable({
  providedIn: 'root',
})
export class GoogleService {
  url: string = 'http://localhost:3000/';

  constructor(private http: HttpClient) {}

  getLocationInfo(location: string): Observable<GoogleResponse> {
    console.log(location);
    const headers = new HttpHeaders({
      'Content-Type': 'application/json',
    });
    return this.http.post<GoogleResponse>(
      this.url + 'googleplace/',
      { name: location },
      { headers: headers }
    );
  }

  getPoi(location: string): Observable<GoogleResponse> {
    console.log(location);
    const headers = new HttpHeaders({
      'Content-Type': 'application/json',
    });
    return this.http.post<GoogleResponse>(
      this.url + 'googlepoi/',
      { name: location },
      { headers: headers }
    );
  }

  getImage(photoRef: string): Observable<string> {
    console.log(photoRef);
    const headers = new HttpHeaders({
      'Content-Type': 'application/json',
    });
    return this.http.post<string>(
      this.url + 'googlephoto/',
      { photoRef: photoRef },
      { headers: headers }
    );
  }


}


Here is my other service which is needed to send and retrieve data from other components

@Injectable({
  providedIn: 'root'
})
export class DataService {

  private googleLocationSource = new Subject<GoogleLocation[]>();

  constructor() { }

  public getLocation(): Observable<GoogleLocation[]> {
    return this.googleLocationSource.asObservable();
  }

  public setLocation(gl: GoogleLocation[]) {
    return this.googleLocationSource.next(gl);
  }
 

Upvotes: 0

Views: 88

Answers (1)

BizzyBob
BizzyBob

Reputation: 14740

My problem is this line this.gs.getImage(loc.photos[0].photo_reference).toString()

Yes, calling toString() on an observable isn't going to work ! :-)

You need to subscribe to that observable to receive its result. There are a few "Higher Order Mapping Operators" that can do this for you, so you don't have to deal with nested subscriptions. In this case, we can use switchMap.

However, it's a little more complex because you want to make a call for each element in the returned array. We can map each location to an observable call to get the image and use forkJoin to create a single observable that emits an array containing the results of the individual observables:

this.ds.getLocation().pipe(
  switchMap(locations => forkJoin(
    locations.map(loc => this.gs.getImage(loc.photos[0].photo_reference))
  )
  .pipe(
    map(imgUrls => imgUrls.map(
      (imgUrl, i) => ({ ...locations[i], imgUrl })
    ))
  ))
)
.subscribe(
    locations => this.listOfPoi = locations
);

The flow here is:

  • switchMap receives the locations and subscribes to observable created by forkJoin
  • forkJoin creates an observable that will emit an array of the results (image urls) from all the individual getImage() calls.
  • map receives the array of image urls and maps it to an array of locations that include the image url
  • subscribe simply receives the final array

Upvotes: 1

Related Questions