1

首先,我想让你们知道,我已经检查了许多堆栈溢出问答,但我找不到正确的解决方案。

我按照 youtube 制作了一个 rails 应用程序。 https://www.youtube.com/watch?v=UtgwdLiJ5hA&t

它运行良好,包括该 youtube 中未涵盖的 markerCluster。

然而,我试图补充的是每个用户都有他或她自己的搜索结果(只有最后一个),并且在点击搜索按钮后,页面将被重定向到具有自动完成位置信息的查询的同一页面。

我成功地用查询重定向了同一个页面,但是在自动完成之后制作与地图对象相同的地图对象太难了。

最接近的答案如下,但它不能完美地工作,因为 AutocompleteService 没有将第一个预测作为我想要的位置返回,即使我在重定向之前放置了选择的确切地址。

如何为 Google Places API 自动完成文本框设置默认值

第二个试验只是复制自动完成对象的某些部分(边界、位置)并在重定向后应用到地图对象。它似乎只对位置起作用,但地图显示结果与边界和看到的区域有问题。

第三次试用是在第二次试用中使用 place_id,但我认为它不会起作用。

我真的很想插入地址文本,在重定向之前选择我选择的地址,并在页面被重定向后立即自动创建自动完成“place_change”事件。但是,我不知道该怎么做。

这是main_map_controller.js(它是刺激js)

import { Controller } from "stimulus"

export default class extends Controller {
    // currentUrl is for redirecting to root_path in javascript
    static targets = ["field", "map", "jsonMarkers", "currentUrl", "east", "north", "south", "west", "lat", "lng", "zoom"];

    connect() {
        if (typeof(google) != "undefined") {
            this.initializeMap();
        }
    }

    initializeMap() {
        this._jason_locations = JSON.parse(this.jsonMarkersTarget.value);
        this.map();
        this.markerCluster();
        this.autocomplete();
        this.placeChanged();
        // this.initialAutocomplete();
        this.setPlace();
        console.log('this.eastTarget.value:', this.eastTarget.value)
    }

    hasQuery() {
        if (this.fieldTarget.value != "" && this.eastTarget.value != "" && this.northTarget.value != "" && this.southTarget.value != "" &&
                this.westTarget.value != "" && this.latTarget.value != "" && this.lngTarget.value != "" && this.zoomTarget.value != ""
            )
            return true;
        else
            return false;
    }

    // Google map initialization
    map() {
        if (this._map == undefined) {
            if (this.hasQuery())
            {
                this._map = new google.maps.Map(this.mapTarget, {
                    center: new google.maps.LatLng(
                        parseFloat(this.latTarget.value),
                        parseFloat(this.lngTarget.value)
                    ),
                    zoom: 13
                });

            } else {
                this._map = new google.maps.Map(this.mapTarget, {
                    center: new google.maps.LatLng(
                        0,
                        0
                    ),
                    zoom: 13
                });
            }
            // Try HTML5 geolocation
            var cur_map = this._map;
            if (navigator.geolocation) {
                navigator.geolocation.getCurrentPosition(function(position) {
                    cur_map.setCenter({
                        lat: position.coords.latitude,
                        lng: position.coords.longitude
                    })
                });
            }
        }
        return this._map;
    }

    // markerCluster() make a group of markers
    markerCluster() {
        let current_map = this.map();
        if (this._marker_cluster == undefined) {
            var markers = this._jason_locations.map((location, i) => {
                var marker = new google.maps.Marker({
                    position: {
                        lat: parseFloat(location["latitude"]),
                        lng: parseFloat(location["longitude"])
                    }
                });
                marker.addListener('click', () => {
                    let infoWindow = new google.maps.InfoWindow({
                        content: `<p>${location.address}</p>`
                    });
                    infoWindow.open(current_map, marker);
                });
                return marker;
            });
            this._marker_cluster = new MarkerClusterer(this.map(),
                markers,
                {imagePath: 'https://cdn.rawgit.com/googlemaps/js-marker-clusterer/gh-pages/images/m'}
            );
        }
        return this._markers_cluster;
    }

    // Autocomplete function. It suggests the full address. 'formatted_address' was added to use user's bad behavior instead of
    // using placeChanged(), but 'formatted_address' saved was not 100% same as the result address of autocomplete, so I didtn' use it.
    // I don't understand why???
    autocomplete() {
        if (this._autocomplete == undefined) {
            this._autocomplete = new google.maps.places.Autocomplete(this.fieldTarget);
            this._autocomplete.bindTo('bounds', this.map());
            this._autocomplete.setFields(['address_components', 'geometry', 'icon', 'name', 'formatted_address', 'place_id']);
            this._autocomplete.addListener('place_changed', this.placeChanged.bind(this));
        }
        return this._autocomplete;
    }

    // If user typed strange word after autocomplete done, we should not allow to search with that word.
    placeChanged() {
        this._place_changed = this.fieldTarget.value;
    }

    // Because AutoComplete cannot have initial place, I had to use another class, AutocompleteService.
    initialAutocomplete() {
        if (this.fieldTarget.value == undefined || this.fieldTarget.value == "")
            return;
        let autocompleteService = new google.maps.places.AutocompleteService();
        let request = { input: this.fieldTarget.value };
        autocompleteService.getPlacePredictions(request, (predictionsArr, placesServiceStatus) => {
            console.log('predictionArr:', predictionsArr);
            console.log('placesServiceStatus:', placesServiceStatus);

            let placeRequest = { placeId: predictionsArr[0].place_id };
            let placeService = new google.maps.places.PlacesService(this.map());
            placeService.getDetails(placeRequest, (placeResult, placeServiceStatus) => {
                console.log('placeResult:', placeResult)
                console.log('placeServiceStatus:', placeServiceStatus);
                this.setPlace(placeResult);
            });
        });
    }

    // setPlace(placeResult) {
    setPlace() {
        // let place = this.autocomplete().getPlace();
        // let place = placeResult;

        if (!this.hasQuery()) {
            return;
        }

        console.log('this.eastTarget.value:', this.eastTarget.value)
        console.log('this.northTarget.value:', this.northTarget.value)
        console.log('this.southTarget.value:', this.southTarget.value)
        console.log('this.westTarget.value:', this.westTarget.value)

        // let bound = {
        //     east: parseFloat(this.eastTarget.value),
        //     north: parseFloat(this.northTarget.value),
        //     south: parseFloat(this.southTarget.value),
        //     west: parseFloat(this.westTarget.value)
        // }
        // console.log('bounds:', bound)

        // // this.map().fitBounds(place.geometry.viewport);
        // // this.map().setCenter(place.geometry.location);
        // this.map().fitBounds(bound);

        // let bounds = this.map().getBounds();
        // console.log('bounds:', bounds)
        let bounds = new google.maps.LatLngBounds(
            new google.maps.LatLng(parseFloat(this.southTarget.value), parseFloat(this.westTarget.value)),
            new google.maps.LatLng(parseFloat(this.northTarget.value), parseFloat(this.eastTarget.value))
        );
        this.map().fitBounds(bounds);
        this.map().setCenter({
            lat: parseFloat(this.latTarget.value),
            lng: parseFloat(this.lngTarget.value)
        });


        let zoom = this.map().getZoom();
        console.log('zoom:', zoom)

        let center = this.map().getCenter();
        console.log('center:', center)

        document.getElementById("search-area").innerHTML = `Near ${this.fieldTarget.value}`;

        this._jason_locations.forEach( location => {
            var position = {
                lat: parseFloat(location["latitude"]),
                lng: parseFloat(location["longitude"])
            }
            console.log('position:', position)
            if (bounds.contains(position)) {
                document.getElementById(location["id"]).classList.remove("d-none")
            } else {
                document.getElementById(location["id"]).classList.add("d-none")
            }

        });
        // this.latitudeTarget.value = place.geometry.location.lat();
        // this.longitudeTarget.value = place.geometry.location.lng();
    }

    reloadMap() {
        let place = this.autocomplete().getPlace();


        console.log(place)

        // this.setPlace(place);

        if (place == undefined || this.fieldTarget.value == "" || this._place_changed != this.fieldTarget.value || !place.geometry) {
            window.alert("Address is invalid!");
            return;
        }

        this.map().fitBounds(place.geometry.viewport);
        this.map().setCenter(place.geometry.location);
        console.log('place.geometry.viewport:', place.geometry.viewport)
        console.log('place.geometry.location:', place.geometry.location)


        let bounds = this.map().getBounds();
        console.log('bounds:', bounds)

        let zoom = this.map().getZoom();
        console.log('zoom:', zoom)

        console.log('place.place_id:', place.place_id)

        // This code was redirect root_path with query, but there was a problem that map was reloaded twice, so removed it.
        // If adding query is not a solution for having each user's recent search history, then what else would it be?
        let jsonParams = { "address": this.fieldTarget.value, ...bounds.toJSON(), ...place.geometry.location.toJSON(), "zoom": zoom.toString() };
        const params = new URLSearchParams(jsonParams);
        console.log(params.toString());

        // Redirect to /posts/?address=xxxxx
        console.log('params:', `${this.currentUrlTarget.value}/?${params.toString()}`);
        window.location.href = `${this.currentUrlTarget.value}/?${params.toString()}`;
        console.log('window.location.href:', window.location.href)
    }

    // prohibit Enter key, only allow to hit the search button.
    preventSubmit(e) {
        if (e.key == "Enter") {
            e.preventDefault();
        }
    }
}
4

1 回答 1

0

是的,我终于通过从以下堆栈溢出站点获得的提示弄明白了。

调用 map.fitBounds 后立即谷歌地图 map.getBounds()

我修复的是修改第二次试验。我没有传递自动完成的 fitBounds() 的结果,而是自动完成本身的视口边界,并且在重定向之后,我在上述解决方案的帮助下将 fitBounds() 之后的所有代码包装到“bound_changed”事件处理程序中。

这是我的固定代码,它运行良好。(对不起未使用的代码和注释。我想留作记录)

import { Controller } from "stimulus"

export default class extends Controller {
    // currentUrl is for redirecting to root_path in javascript
    static targets = ["field", "map", "jsonMarkers", "currentUrl", "east", "north", "south", "west", "lat", "lng"];

    connect() {
        if (typeof(google) != "undefined") {
            this.initializeMap();
        }
    }

    initializeMap() {
        this._jason_locations = JSON.parse(this.jsonMarkersTarget.value);
        this.map();
        this.markerCluster();
        this.autocomplete();
        this.placeChanged();
        // this.initialAutocomplete();
        this.setPlace();
        console.log('this.eastTarget.value:', this.eastTarget.value)
    }

    hasQuery() {
        if (this.fieldTarget.value != "" && this.eastTarget.value != "" && this.northTarget.value != "" && this.southTarget.value != "" &&
                this.westTarget.value != "" && this.latTarget.value != "" && this.lngTarget.value != ""
            )
            return true;
        else
            return false;
    }

    // Google map initialization
    map() {
        if (this._map == undefined) {
            if (this.hasQuery())
            {
                this._map = new google.maps.Map(this.mapTarget, {
                    center: new google.maps.LatLng(
                        parseFloat(this.latTarget.value),
                        parseFloat(this.lngTarget.value)
                    )
                    // zoom: parseInt(this.zoomTarget.value)
                });

            } else {
                this._map = new google.maps.Map(this.mapTarget, {
                    center: new google.maps.LatLng(
                        0,
                        0
                    ),
                    zoom: 13
                });
            }
            // Try HTML5 geolocation
            var cur_map = this._map;
            if (navigator.geolocation) {
                navigator.geolocation.getCurrentPosition(function(position) {
                    cur_map.setCenter({
                        lat: position.coords.latitude,
                        lng: position.coords.longitude
                    })
                });
            }
        }
        return this._map;
    }

    // markerCluster() make a group of markers
    markerCluster() {
        let current_map = this.map();
        if (this._marker_cluster == undefined) {
            var markers = this._jason_locations.map((location, i) => {
                var marker = new google.maps.Marker({
                    position: {
                        lat: parseFloat(location["latitude"]),
                        lng: parseFloat(location["longitude"])
                    }
                });
                marker.addListener('click', () => {
                    let infoWindow = new google.maps.InfoWindow({
                        content: `<p>${location.address}</p>`
                    });
                    infoWindow.open(current_map, marker);
                });
                return marker;
            });
            this._marker_cluster = new MarkerClusterer(this.map(),
                markers,
                {imagePath: 'https://cdn.rawgit.com/googlemaps/js-marker-clusterer/gh-pages/images/m'}
            );
        }
        return this._markers_cluster;
    }

    // Autocomplete function. It suggests the full address. 'formatted_address' was added to use user's bad behavior instead of
    // using placeChanged(), but 'formatted_address' saved was not 100% same as the result address of autocomplete, so I didtn' use it.
    // I don't understand why???
    autocomplete() {
        if (this._autocomplete == undefined) {
            this._autocomplete = new google.maps.places.Autocomplete(this.fieldTarget);
            this._autocomplete.bindTo('bounds', this.map());
            this._autocomplete.setFields(['address_components', 'geometry', 'icon', 'name']);
            this._autocomplete.addListener('place_changed', this.placeChanged.bind(this));
        }
        return this._autocomplete;
    }

    // If user typed strange word after autocomplete done, we should not allow to search with that word.
    placeChanged() {
        this._place_changed = this.fieldTarget.value;
    }

    // Because AutoComplete cannot have initial place, I had to use another class, AutocompleteService.
    initialAutocomplete() {
        if (this.fieldTarget.value == undefined || this.fieldTarget.value == "")
            return;
        let autocompleteService = new google.maps.places.AutocompleteService();
        let request = { input: this.fieldTarget.value };
        autocompleteService.getPlacePredictions(request, (predictionsArr, placesServiceStatus) => {
            console.log('predictionArr:', predictionsArr);
            console.log('placesServiceStatus:', placesServiceStatus);

            let placeRequest = { placeId: predictionsArr[0].place_id };
            let placeService = new google.maps.places.PlacesService(this.map());
            placeService.getDetails(placeRequest, (placeResult, placeServiceStatus) => {
                console.log('placeResult:', placeResult)
                console.log('placeServiceStatus:', placeServiceStatus);
                this.setPlace(placeResult);
            });
        });
    }

    // setPlace(placeResult) {
    setPlace() {
        // let place = this.autocomplete().getPlace();
        // let place = placeResult;

        if (!this.hasQuery()) {
            return;
        }

        console.log('this.eastTarget.value:', this.eastTarget.value)
        console.log('this.northTarget.value:', this.northTarget.value)
        console.log('this.southTarget.value:', this.southTarget.value)
        console.log('this.westTarget.value:', this.westTarget.value)

        // let bound = {
        //     east: parseFloat(this.eastTarget.value),
        //     north: parseFloat(this.northTarget.value),
        //     south: parseFloat(this.southTarget.value),
        //     west: parseFloat(this.westTarget.value)
        // }
        // console.log('bounds:', bound)

        // // this.map().fitBounds(place.geometry.viewport);
        // // this.map().setCenter(place.geometry.location);
        // this.map().fitBounds(bound);

        // let bounds = this.map().getBounds();
        // console.log('bounds:', bounds)
        let bounds = new google.maps.LatLngBounds(
            new google.maps.LatLng(parseFloat(this.southTarget.value), parseFloat(this.westTarget.value)),
            new google.maps.LatLng(parseFloat(this.northTarget.value), parseFloat(this.eastTarget.value))
        );
        this.map().fitBounds(bounds);

        google.maps.event.addListenerOnce(this.map(), 'bounds_changed', () => {
            this.map().setCenter({
                lat: parseFloat(this.latTarget.value),
                lng: parseFloat(this.lngTarget.value)
            });

            bounds = this.map().getBounds();
            console.log('bounds:', bounds)

            let zoom = this.map().getZoom();
            console.log('zoom:', zoom)

            let center = this.map().getCenter();
            console.log('center:', center)

            document.getElementById("search-area").innerHTML = `Near ${this.fieldTarget.value}`;

            this._jason_locations.forEach( location => {
                var position = {
                    lat: parseFloat(location["latitude"]),
                    lng: parseFloat(location["longitude"])
                }
                console.log('position:', position)
                if (bounds.contains(position)) {
                    document.getElementById(location["id"]).classList.remove("d-none")
                } else {
                    document.getElementById(location["id"]).classList.add("d-none")
                }

            });
            // this.latitudeTarget.value = place.geometry.location.lat();
            // this.longitudeTarget.value = place.geometry.location.lng();
        })
    }

    reloadMap() {
        let place = this.autocomplete().getPlace();
        console.log(place)

        // this.setPlace(place);

        if (place == undefined || this.fieldTarget.value == "" || this._place_changed != this.fieldTarget.value || !place.geometry) {
            window.alert("Address is invalid!");
            return;
        }

        // This code was redirect root_path with query, but there was a problem that map was reloaded twice, so removed it.
        // If adding query is not a solution for having each user's recent search history, then what else would it be?
        // let jsonParams = { "address": this.fieldTarget.value, ...bounds.toJSON(), ...place.geometry.location.toJSON(), "zoom": zoom.toString() };
        let jsonParams = { "address": this.fieldTarget.value, ...place.geometry.viewport.toJSON(), ...place.geometry.location.toJSON()};

        const params = new URLSearchParams(jsonParams);

        // Redirect to /posts/?address=xxxxx
        window.location.href = `${this.currentUrlTarget.value}/?${params.toString()}`;
    }

    // prohibit Enter key, only allow to hit the search button.
    preventSubmit(e) {
        if (e.key == "Enter") {
            e.preventDefault();
        }
    }
}
于 2020-06-12T01:18:29.957 回答