Javascript 如何保存Google Place自动完成的结果
首先,我想让你们知道,我已经检查了许多堆栈溢出Q&A,但我无法得到正确的解决方案 我跟随youtube制作了一个rails应用程序。 它工作得很好,包括那个youtube上没有报道的markerCluster 然而,我试图添加的是,每个用户都有自己的搜索结果(只有最后一个),点击搜索按钮后,页面将重定向到同一页面,其中包含有关自动完成位置的信息。 我成功地重定向了带有查询的同一个页面,但是很难创建与自动完成后的页面相同的映射对象 最接近的答案如下所示,但它并不完美,因为AutocompleteService没有将第一个预测返回到我想要的位置,即使我在重定向之前输入了所选的确切地址 第二次尝试只是复制自动完成对象的某些部分(边界、位置),并在重定向后应用于map对象。它似乎只在位置上起作用,但地图显示结果与边界和看到的区域不符 第三次试验是在第二次试验中使用place_id,但我认为它不起作用 我真的想插入地址文本,选择重定向前选择的地址,并在页面重定向后自动创建自动完成“地点更改”事件。然而,我不知道该怎么做 这是主的_map_controller.js(它是刺激js)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
从“刺激”导入{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();
}
}
}