Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/442.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 如何保存Google Place自动完成的结果_Javascript_Ruby On Rails_Google Maps Api 3_Stimulusjs - Fatal编程技术网

Javascript 如何保存Google Place自动完成的结果

Javascript 如何保存Google Place自动完成的结果,javascript,ruby-on-rails,google-maps-api-3,stimulusjs,Javascript,Ruby On Rails,Google Maps Api 3,Stimulusjs,首先,我想让你们知道,我已经检查了许多堆栈溢出Q&A,但我无法得到正确的解决方案 我跟随youtube制作了一个rails应用程序。 它工作得很好,包括那个youtube上没有报道的markerCluster 然而,我试图添加的是,每个用户都有自己的搜索结果(只有最后一个),点击搜索按钮后,页面将重定向到同一页面,其中包含有关自动完成位置的信息。 我成功地重定向了带有查询的同一个页面,但是很难创建与自动完成后的页面相同的映射对象 最接近的答案如下所示,但它并不完美,因为Autocomplete

首先,我想让你们知道,我已经检查了许多堆栈溢出Q&A,但我无法得到正确的解决方案

我跟随youtube制作了一个rails应用程序。

它工作得很好,包括那个youtube上没有报道的markerCluster

然而,我试图添加的是,每个用户都有自己的搜索结果(只有最后一个),点击搜索按钮后,页面将重定向到同一页面,其中包含有关自动完成位置的信息。

我成功地重定向了带有查询的同一个页面,但是很难创建与自动完成后的页面相同的映射对象

最接近的答案如下所示,但它并不完美,因为AutocompleteService没有将第一个预测返回到我想要的位置,即使我在重定向之前输入了所选的确切地址

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

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

我真的想插入地址文本,选择重定向前选择的地址,并在页面重定向后自动创建自动完成“地点更改”事件。然而,我不知道该怎么做

这是主的_map_controller.js(它是刺激js)

从“刺激”导入{Controller}
导出默认类扩展控制器{
//currentUrl用于重定向到javascript中的根路径
静态目标=[“字段”、“地图”、“jsonMarkers”、“当前URL”、“东”、“北”、“南”、“西”、“纬度”、“液化天然气”、“缩放”];
连接(){
if(typeof(google)!=“未定义”){
这个.initializeMap();
}
}
初始化映射(){
this.\u jason\u locations=JSON.parse(this.jsonMarkersTarget.value);
这个.map();
这个.markerCluster();
这是一个.autocomplete();
这个.placeChanged();
//this.initialAutocomplete();
这个.setPlace();
console.log('this.eastTarget.value:',this.eastTarget.value)
}
hasQuery(){
如果(this.fieldTarget.value!=“”&this.eastTarget.value!=“”&this.northTarget.value!=“”&this.southTarget.value!=“”)&&
this.westTarget.value!=“”&this.latTarget.value!=“”&this.lngTarget.value!=“”&this.zoomTarget.value!=“”
)
返回true;
其他的
返回false;
}
//谷歌地图初始化
地图(){
if(this.\u map==未定义){
if(this.haskquery())
{
this.\u map=new google.maps.map(this.mapTarget{
中心:新google.maps.LatLng(
parseFloat(this.latTarget.value),
parseFloat(this.lngTarget.value)
),
缩放:13
});
}否则{
this.\u map=new google.maps.map(this.mapTarget{
中心:新google.maps.LatLng(
0,
0
),
缩放:13
});
}
//试试HTML5地理定位
var cur_map=此。_map;
if(导航器.地理位置){
navigator.geolocation.getCurrentPosition(函数(位置){
当前地图设置中心({
纬度:位置坐标纬度,
lng:position.coords.longitude
})
});
}
}
返回此。\u地图;
}
//markerCluster()生成一组标记
markerCluster(){
让current_map=this.map();
如果(此._标记_集群==未定义){
var markers=此.\u jason\u locations.map((位置,i)=>{
var marker=new google.maps.marker({
职位:{
lat:parseFloat(位置[“纬度]),
lng:parseFloat(位置[“经度”])
}
});
marker.addListener('单击',()=>{
让infoWindow=new google.maps.infoWindow({
内容:`${location.address}

` }); 打开(当前地图、标记); }); 返回标记; }); this.\u marker\u cluster=new MarkerClusterer(this.map(), 标记, {imagePath:'https://cdn.rawgit.com/googlemaps/js-marker-clusterer/gh-pages/images/m'} ); } 返回此。\u标记\u群集; } //自动完成功能。它建议完整地址。添加“格式化地址”是为了使用用户的不良行为,而不是 //使用placeChanged(),但保存的“格式化地址”与自动完成的结果地址不完全相同,因此我没有使用它。 //我不明白为什么??? 自动完成(){ if(this.\u autocomplete==未定义){ this.\u autocomplete=new google.maps.places.autocomplete(this.fieldTarget); this._autocomplete.bindTo('bounds',this.map()); 这个.u autocomplete.setFields(['address\u components'、'geometry'、'icon'、'name'、'formatted\u address'、'place\u id']); this.\u autocomplete.addListener('place\u changed',this.placeChanged.bind(this)); } 返回此。\u自动完成; } //如果用户在自动完成后输入了奇怪的单词,我们不应该允许使用该单词进行搜索。 placeChanged(){ this.\u place\u changed=this.fieldTarget.value; } //因为AutoComplete不能有初始位置,所以我不得不使用anot
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();
        }
    }
}
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();
        }
    }
}