0

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);
  }
 
3
  • Presumably getImage returns an observable of the data, and it's unclear why you thought toString would magically make it synchronous. Please give a minimal reproducible example. Commented Mar 30, 2021 at 21:01
  • Yeah I know... How can I get the string value of that? I tried with pipe, forkjoin, map, switchmap and so on but not working.. Okay! I provided more information, does that help? Commented Mar 30, 2021 at 21:30
  • It's unclear what you've tried, I'd expect forkJoin to work (given you want to fan out to multiple observables then get all of the results). Commented Mar 30, 2021 at 21:34

1 Answer 1

1

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
Sign up to request clarification or add additional context in comments.

4 Comments

I see thanks. Indeed I want to call for each element, so it's a bit complicated. I tried your solution but it says that locations.map is of type unknown also Property 'pipe' does not exist on type 'OperatorFunction<unknown, unknown[]>'. this.ds.getLocation() will return an Observable from type GoogleLocation[] btw
I added some more information, I don't know if they help.. Thanks again
looks like I had a misplaced paren. updated!
Thank you a lot! It seems to work and no error!! Just getting CORS problem now but this is another problem:) Thanks again!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.