Chiwda
Chiwda

Reputation: 1344

Angular 9 - Property 'subscribe' does not exist on type 'void'

I am getting the following error:

Property 'subscribe' does not exist on type 'void'.ts(2339)

when I try to use my AutoLocate function (below) by subscribing to it thusly:

this.AutoLocate().subscribe(data => { this.GotLoc = data});

Below is the AutoLocate function and also a GetAddress function that I use to get a complete model that uses Lat/Long and Address.

  AutoLocate() {
    let AutoLocatedLocation: PlaceLocation;
    if(!Capacitor.isPluginAvailable('Geolocation')) {
      this.Alert.create({header: 'Location Service Error', message: 'Could not initialize Location Services! Please call support!'})
      return;
    }
    Plugins.Geolocation.getCurrentPosition().then(GeoPosition => {
      return this.coordinates = {lat: GeoPosition.coords.latitude, lng: GeoPosition.coords.longitude};
    }).then(coords => {
      this.GetAddress(coords.lat, coords.lng).subscribe(res => {
        this.Address = res;
        return AutoLocatedLocation = { lat: coords.lat, lng: coords.lng, address: res};
      });
    }).catch(error => {
      this.Alert.create({header: 'Location Service Error', message: 'Could not initialize Location Services! Please call support!'})
      console.log("Location: ", error);
    });
  }

  private GetAddress(lat: number, lng: number) {
    return this.http.get<any>(`https://maps.googleapis.com/maps/api/geocode/json?latlng=${lat},${lng}&key=${environment.googleMapsAPIKey}`)
    .pipe(map(geoData => {
      if (!geoData || !geoData.results || geoData.results.length === 0) {
        return null;
      }
      return geoData.results[0].formatted_address;
    }));
  }
  

What am I doing wrong?

Upvotes: 0

Views: 315

Answers (1)

Barremian
Barremian

Reputation: 31125

It appears you're mixing Promises with Observables. Moreover the AutoLocate() function isn't returning anything at the moment.

You could either convert observable to promise or vice-versa. I'd do the former using RxJS from function. Then you could apply the necessary operators to transform the data

import { of, from, NEVER } from 'rxjs';
import { catchError, map, switchMap } from 'rxjs/operators';

AutoLocate(): Observable<any> {     // <-- return type `Observable`
  let AutoLocatedLocation: PlaceLocation;
  if(!Capacitor.isPluginAvailable('Geolocation')) {
    this.Alert.create({header: 'Location Service Error', message: 'Could not initialize Location Services! Please call support!'})
    return NEVER;       // <-- use RxJS `NEVER` constant - it'll never emit
  }
  
  return from(Plugins.Geolocation.getCurrentPosition()).pipe(  // <-- return the observable
    switchMap(GeoPosition => {
      const coords = { lat: GeoPosition.coords.latitude, lng: GeoPosition.coords.longitude };
      this.coordinates = coords;  // <-- why is this needed though?
      return this.GetAddress(coords.lat, coords.lng).pipe(
        map(res => {
          this.Address = res;     // <-- again, is this needed here?
          return ({ ...coords, address: res });  // spread operator `...` retains old values while adding/modifying other values
        })
      )
    }),
    catchError(error => {
      this.Alert.create({header: 'Location Service Error', message: 'Could not initialize Location Services! Please call support!'});
      return of(error);           // <-- `catchError` must return an observable
    })
  );
}

Breakdown:

  1. from function to convert the promise to observable
  2. NEVER constant to not emit to the subscription instead of a plain JS return;
  3. map operator to transform the emission to the required format
  4. switchMap operator to map from one observable to another
  5. catchError operator to catch and trigger an alert. Note that catchError must return an observable. of function is used for that function.

Upvotes: 3

Related Questions