BA1995
BA1995

Reputation: 415

Google Places API - Angular 2 / Typescript / Ionic 2

Has anyone ever converted this is into typescript?

  var map;
  var infowindow;

  function initMap() {
    var pyrmont = {lat: -33.867, lng: 151.195};

    map = new google.maps.Map(document.getElementById('map'), {
      center: pyrmont,
      zoom: 15
    });

    infowindow = new google.maps.InfoWindow();
    var service = new google.maps.places.PlacesService(map);
    service.nearbySearch({
      location: pyrmont,
      radius: 500,
      type: ['store']
    }, callback);
  }

  function callback(results, status) {
    if (status === google.maps.places.PlacesServiceStatus.OK) {
      for (var i = 0; i < results.length; i++) {
        createMarker(results[i]);
      }
    }
  }

  function createMarker(place) {
    var placeLoc = place.geometry.location;
    var marker = new google.maps.Marker({
      map: map,
      position: place.geometry.location
    });

    google.maps.event.addListener(marker, 'click', function() {
      infowindow.setContent(place.name);
      infowindow.open(map, this);
    });
  }

I have tried to convert it myself as seen below but I am getting an error in console that I will show below the code, If anyone could give a heads up where im going wrong it would be great

  constructor(public navCtrl: NavController, private navParams: NavParams, private ngZone: NgZone) {}

  ionViewDidLoad() {
    console.log('ionViewDidLoad DetailsPage');
    this.loadMap();
  }

  @ViewChild('map') mapElement: ElementRef;
  map: any;

  loadMap(){

    Geolocation.getCurrentPosition().then((position) => {

      let latLng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);

      let mapOptions = {
        center: latLng,
        zoom: 15,
        mapTypeId: google.maps.MapTypeId.ROADMAP
      }

      this.map = new google.maps.Map(this.mapElement.nativeElement, mapOptions);

      let service = new google.maps.places.PlacesService(mapOptions);

      service.nearbySearch({
        location: latLng,
        radius: 500,
        type: ['pub']
      }, (results, status) => {
          this.callback(results, status)
      });

    }, (err) => {
      console.log(err);
    });

  }

  callback(results, status) {
    if (status === google.maps.places.PlacesServiceStatus.OK) {
      for (var i = 0; i < results.length; i++) {
        this.createMarker(results[i]);
      }
    }
  }

  createMarker(place){
    var placeLoc = place.geometry.location;
    var marker = new google.maps.Marker({
        map: this.map,
        position: place.geometry.location
    });

    let infowindow = new google.maps.InfoWindow();

    google.maps.event.addListener(marker, 'click', () => {
      this.ngZone.run(() => {
        infowindow.setContent(place.name);
        infowindow.open(this.map,this);
      });
    });
  }

  addMarker(){

    let marker = new google.maps.Marker({
      map: this.map,
      animation: google.maps.Animation.DROP,
      position: this.map.getCenter()
    });

    let content = "<h4>You are here!</h4>";          

    this.addInfoWindow(marker, content);

  }

  addInfoWindow(marker, content){

    let infoWindow = new google.maps.InfoWindow({
      content: content
    });

    google.maps.event.addListener(marker, 'click', () => {
      this.ngZone.run(() => {
        infoWindow.open(this.map, marker);
      });
    });
  }

error in console

Uncaught TypeError: this.b.getElementsByTagName is not a function
    at B5.attributionText_changed (places_impl.js:28)
    at zb (js?key=AIzaSyAZTATsl9JjVKodXRnzeSk52Ndvtz-HPPU&libraries=places:36)
    at B5._.y.bindTo (js?key=AIzaSyAZTATsl9JjVKodXRnzeSk52Ndvtz-HPPU&libraries=places:104)
    at Object.f6.f (places_impl.js:38)
    at Hw.<anonymous> (js?key=AIzaSyAZTATsl9JjVKodXRnzeSk52Ndvtz-HPPU&libraries=places:137)
    at js?key=AIzaSyAZTATsl9JjVKodXRnzeSk52Ndvtz-HPPU&libraries=places:105
    at Object.<anonymous> (js?key=AIzaSyAZTATsl9JjVKodXRnzeSk52Ndvtz-HPPU&libraries=places:37)
    at js?key=AIzaSyAZTATsl9JjVKodXRnzeSk52Ndvtz-HPPU&libraries=places:105
    at js?key=AIzaSyAZTATsl9JjVKodXRnzeSk52Ndvtz-HPPU&libraries=places:37
    at js?key=AIzaSyAZTATsl9JjVKodXRnzeSk52Ndvtz-HPPU&libraries=places:105

Specifically as highlighted in console file.

b.getElementsByTagName("a"),c=0;c<_.w(b);c++)b[c].style.color="#444";this.H&&this.H.set("placesDataProviders",a)};B5.prototype.hide_changed=function(){_.kA(this.b,!this.get("hide"))};_.t(D5,_.y);_.k=D5.prototype;_.k.input_changed=function(){window.clearTimeout(this.f);this.f=window.setTimeout((0,_.p)(this.Ol,this),100)};_.k.Ol=function(){var a=this.l,b=this.Lb();a!=b&&(H5(this),this.l=b);this.f=null};_.k.Jm=function(){if(this.Tc())J5(this,this.Lb());else{var a={name:this.Lb()};this.Ef(a)}};

Upvotes: 4

Views: 3765

Answers (3)

jacksonet00
jacksonet00

Reputation: 19

I hope this helps. I converted all the relavent types to typescript for a recent project.

Also, as you can see in my application's backend a custom url is appended to the PlaceResult so I'm extending the type for my usecase using an interface.

// docs: https://developers.google.com/maps/documentation/places/web-service/search-nearby#nearby-search-responses

export interface RRPlaceResult extends PlaceResult {
    url: string;
}

export type PlaceResult = {
    html_attributions: string[];
    results: Place[];
    status: PlacesSearchStatus;
    error_message?: string;
    info_messages?: string[];
    next_page_token?: string;
};

export type Place = {
    address_components?: AddressComponent[];
    adr_adress?: string;
    business_status?: string;
    curbside_pickup?: boolean;
    current_opening_hours?: PlaceOpeningHours;
    delivery?: boolean;
    dine_in?: boolean;
    editorial_summary?: PlaceEditorialSummary;
    formatted_address?: string;
    formatted_phone_number?: string;
    geometry?: Geometry;
    icon?: string;
    icon_background_color?: string;
    icon_mask_base_uri?: string;
    international_phone_number?: string;
    name?: string;
    opening_hours?: PlaceOpeningHours;
    permanently_closed?: boolean; // deprecated
    photos?: PlacePhoto[];
    place_id?: string;
    plus_code?: PlusCode;
    price_level?: number;
    rating?: number;
    reference?: string; // deprecated
    reservable?: boolean;
    reviews?: PlaceReview[];
    scope?: string; //deprecated
    secondary_opening_hours?: PlaceOpeningHours[];
    serves_beer?: boolean;
    serves_breakfast?: boolean;
    serves_brunch?: boolean;
    serves_dinner?: boolean;
    serves_lunch?: boolean;
    serves_vegetarian_food?: boolean;
    serves_wine?: boolean;
    takeout?: boolean;
    types?: string[];
    url?: string;
    user_ratings_total?: number;
    utc_offset?: number;
    vicinity?: string;
    website?: string;
    wheelchair_accessible_entrance?: boolean;
};

export type PlacesSearchStatus = 'OK' | 'ZERO_REULTS' |
    'INVALID_REQUEST' | 'OVER_QUERY_LIMIT' | 'REQUEST_DENIED' |
    'UNKNOWN_ERROR';

export type AddressComponent = {
    long_name: string;
    short_name: string;
    types: string[];
};

export type PlaceEditorialSummary = {
    language?: string;
    overview?: string;
};

export type Geometry = {
    location: LatLngLiteral;
    viewport: Bounds;
};

export type LatLngLiteral = {
    lat: number;
    lng: number;
};

export type Bounds = {
    northeast: LatLngLiteral;
    southwest: LatLngLiteral;
};

export type PlaceOpeningHours = {
    open_now?: boolean;
    periods?: PlaceOpeningHoursPeriod[];
    special_days?: PlaceSpecialDay[];
    type?: string;
    weekday_text?: string[];
};

export type PlaceOpeningHoursPeriod = {
    open: PlaceOpeningHoursPeriodDetail;
    close?: PlaceOpeningHoursPeriodDetail;
};

export type PlaceSpecialDay = {
    date?: string;
    exceptional_hours?: boolean;
};

export type PlaceOpeningHoursPeriodDetail = {
    day: number;
    time: string;
    date?: string;
    truncated?: boolean;
};

export type PlacePhoto = {
    height: number;
    html_attributions: string[];
    photo_reference: string;
    width: number;
};

export type PlusCode = {
    global_code: string;
    compound_code?: string;
};

export type PlaceReview = {
    author_name: string;
    rating: number;
    relative_time_description: string;
    time: number;
    author_url?: string;
    language?: string;
    original_language?: string;
    profile_photo_url?: string;
    text?: string;
    translated?: boolean;
};

Upvotes: 0

Tony Kelly
Tony Kelly

Reputation: 11

I just wanted to give an update to this answer. I am using this code in a application written in Ionic which is a Angular based library. I was able to use this code and get it to work but I had to change one thing. The infowindow would not open and kept throwing the Uncaught TypeError: this.b.getElementsByTagName is not a function error. In order to fix this I had to change

google.maps.event.addListener(marker, 'click', () => {
  this.ngZone.run(() => {
    infowindow.setContent(place.name);
    infowindow.open(this.map,this);
  });

to

google.maps.event.addListener(marker, 'click', () => {
      this.ngZone.run(() => {
        infowindow.setContent(place.name);
        infowindow.open(this.map,this.infowindow);
      });

adding

this.infowindow

was the key. Hope this helps someone who ran into the same problem as me

Upvotes: 1

Poul Kruijt
Poul Kruijt

Reputation: 71911

The this context is lost if you add a callback function like that. There are multiple ways to solve this. One is to use an anonymous function wrapper:

service.nearbySearch({
  location: latLng,
  radius: 500,
  type: ['pub']
}, (results, status) => {
  this.callback(results, status);
});

Also be aware that a change detection cycle is not triggered when you use the addListener calls from google.maps. Check this answer for more information. You might run into that problem with your addInfoWindow function:

@Component({
    selector : 'maps-test',
    template : '<div #map></div>'
})
export class MapsTestComponent {

  @ViewChild('map')
  mapElement: ElementRef;

  constructor(private ngZone: NgZone){}

  ngAfterViewInit() {
      loadMap();
  }

  loadMap(){

    Geolocation.getCurrentPosition().then((position) => {

      let latLng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);

      let mapOptions = {
        center: latLng,
        zoom: 15,
        mapTypeId: google.maps.MapTypeId.ROADMAP
      }

      this.map = new google.maps.Map(this.mapElement.nativeElement, mapOptions);

            let service = new google.maps.places.PlacesService(this.map);


      service.nearbySearch({
        location: latLng,
        radius: 500,
        type: ['pub']
      }, (results, status) => {
          this.callback(results, status)
      });

    }, (err) => {
      console.log(err);
    });

  }

  callback(results, status) {
    if (status === google.maps.places.PlacesServiceStatus.OK) {
      for (var i = 0; i < results.length; i++) {
        this.createMarker(results[i]);
      }
    }
  }

  createMarker(place){
    var placeLoc = place.geometry.location;
    var marker = new google.maps.Marker({
        map: this.map,
        position: place.geometry.location
    });

    let infowindow = new google.maps.InfoWindow();

    google.maps.event.addListener(marker, 'click', () => {
      this.ngZone.run(() => {
        infowindow.setContent(place.name);
        infowindow.open(this.map,this);
      });
    });
  }

  addMarker(){

    let marker = new google.maps.Marker({
      map: this.map,
      animation: google.maps.Animation.DROP,
      position: this.map.getCenter()
    });

    let content = "<h4>You are here!</h4>";          

    this.addInfoWindow(marker, content);

  }

  addInfoWindow(marker, content){

    let infoWindow = new google.maps.InfoWindow({
      content: content
    });

    google.maps.event.addListener(marker, 'click', () => {
      this.ngZone.run(() => {
        infoWindow.open(this.map, marker);
      });
    });
  }
}

Upvotes: 4

Related Questions