Google Maps Places Autocomplete - Uncaught TypeErr

2019-07-31 23:34发布

问题:

setAutocomplete() {
    this.originPlaceId = null;
    this.destinationPlaceId = null;
    this.travelMode = google.maps.TravelMode.WALKING;
    this.directionsDisplay.setMap(this.map);

    this.setMapControls(this.map);

    this.setupClickListener('changemode-walking', google.maps.TravelMode.WALKING);
    this.setupClickListener('changemode-transit', google.maps.TravelMode.TRANSIT);
    this.setupClickListener('changemode-driving', google.maps.TravelMode.DRIVING);

    console.log(this.originInput);
    this.originAutocomplete = new google.maps.places.Autocomplete(this.originInput);
    this.originAutocomplete.bindTo('bounds', this.map);
    console.log(this.originAutocomplete);
    this.originAutocomplete.addListener('place_changed', function() {
        console.log(this.originAutocomplete);
        var place = this.originAutocomplete.getPlace();
        console.log("here", place);
        if (!place.geometry) {
            window.alert("Autocomplete's returned place contains no geometry");
            return;
        }
        this.expandViewportToFitPlace(this.map, place);

        // If the place has a geometry, store its place ID and route if we have
        // the other place ID
        this.originPlaceId = place.place_id;
        this.route(this.directionsService, this.directionsDisplay);
    });

    this.destinationAutocomplete = new google.maps.places.Autocomplete(this.destinationPlaceInput);
    this.destinationAutocomplete.bindTo('bounds',this.map);
    this.destinationAutocomplete.addListener('place_changed', function() {
        var place = this.destinationAutocomplete.getPlace();
        if (!place.geometry) {
            window.alert("Autocomplete's returned place contains no geometry");
            return;
        }
        this.expandViewportToFitPlace(this.map, place);

        // If the place has a geometry, store its place ID and route if we have
        // the other place ID
        this.destinationPlaceId = place.place_id;
        this.route(this.directionsService, this.directionsDisplay);
        //this.getNearbyPlaces(this.destinationPlaceId, 5000);
    });
};
setMapControls(map) {
    this.originInput = document.getElementById('origin-input');
    this.destinationPlaceInput = document.getElementById('destination-input');
    this.modes = document.getElementById('mode-selector');

    map.controls[google.maps.ControlPosition.TOP_LEFT].push(this.originInput);
    map.controls[google.maps.ControlPosition.TOP_LEFT].push(this.destinationPlaceInput);
    map.controls[google.maps.ControlPosition.TOP_LEFT].push(this.modes);
};
<input id="origin-input" class="controls" type="text" force-selection="true" placeholder="Enter an origin location">
<input id="destination-input" class="controls" type="text" force-selection="true" placeholder="Enter a destination location">
<div id="mode-selector" class="controls">
    <input type="radio" name="type" id="changemode-walking" checked="checked">
    <label for="changemode-walking">Walking</label>
    <input type="radio" name="type" id="changemode-transit">
    <label for="changemode-transit">Transit</label>
    <input type="radio" name="type" id="changemode-driving">
    <label for="changemode-driving">Driving</label>
</div>
<div id="directionsList"></div>
<div id="map_canvas"></div>

I have these functions which are supposed to give me the directions A to B from Google Maps Places from the input fields (originInput and destinationPlaceInput).

After I select an input I have this error in console:

Uncaught TypeError: Cannot read property 'getPlace' of undefined

This error is thrown after the "place_changed" event triggers.

回答1:

Inside the place_changed-callback the keyword this points to the object which has been triggered the event.

so you may simply use

var place = this.getPlace();

instead of

var place = this.destinationAutocomplete.getPlace();


回答2:

How to keep the context/this of the controller?

tldr:

Create a function that returns a function with the context:

this.searchBox.addListener('places_changed', ((that) => () => this.placesChanged(that))(this));

Explanation

Like Dr.Molle points out: this points to the object which has triggered the event, namely the element on which you want to add an event listener on.

The addListener takes a function declaration. To keep access on your controller's this, you could also create a closure that passes the controller's this into the function, like so:

this.searchBox.addListener('places_changed', ((that) => () => this.placesChanged(that))(this));

Here I defined a function that takes a parameter called that, and upon execution returns a function that contains the this. It is a closure because the inner function's scope keeps a reference to the that variable from the outer functions scope (and other variables that could get passed to this.placesChanged).

Now if a event places_changed is emitted, the function declaration is ran. I am still able to change properties on the that variable inside the this.placesChanged function. This is possible because in objects are passed by reference (whereas primitive types are passed by values).