I just learned how to use google maps with javascript in Livewire. I did a test project and everything works fine for me. When trying to move my project to a productive project I receive the following error:
Livewire.emit is not a function
I am using google search box:
<input id="pac-input" class="controls" type="text" placeholder="Search Box" />
<div id="map"></div>
The search performs well. But I need to send the latitude and longitude to a Livewire listener
Livewire.emit('getLatitude', place.geometry['location'].lat());
Livewire.emit('getLongitud', place.geometry['location'].lng());
As I mentioned in the test project it works well for me, in the productive one it does not. Check that the main tags are DIV, I tried using #push #livewirescripts and I can't detect the problem
the full javascript code I use:
<script>
function initAutocomplete() {
const map = new google.maps.Map(document.getElementById("map"), {
center: { lat: -35.2385, lng: -65.6740 },
zoom: 6,
mapTypeId: "roadmap",
});
// Create the search box and link it to the UI element.
const input = document.getElementById("pac-input");
const searchBox = new google.maps.places.SearchBox(input);
map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);
// Bias the SearchBox results towards current map's viewport.
map.addListener("bounds_changed", () => {
searchBox.setBounds(map.getBounds());
});
let markers = [];
// Listen for the event fired when the user selects a prediction and retrieve
// more details for that place.
searchBox.addListener("places_changed", () => {
const places = searchBox.getPlaces();
if (places.length == 0) {
return;
}
// Clear out the old markers.
markers.forEach((marker) => {
marker.setMap(null);
});
markers = [];
// For each place, get the icon, name and location.
const bounds = new google.maps.LatLngBounds();
places.forEach((place) => {
if (!place.geometry || !place.geometry.location) {
console.log("Returned place contains no geometry");
return;
}
const icon = {
url: place.icon,
size: new google.maps.Size(71, 71),
origin: new google.maps.Point(0, 0),
anchor: new google.maps.Point(17, 34),
scaledSize: new google.maps.Size(25, 25),
};
// Create a marker for each place.
markers.push(
new google.maps.Marker({
map,
icon,
title: place.name,
position: place.geometry.location,
})
);
if (place.geometry.viewport) {
// Only geocodes have viewport.
bounds.union(place.geometry.viewport);
} else {
bounds.extend(place.geometry.location);
}
/* document.getElementById('lat-span').value = (place.geometry['location'].lat());
document.getElementById('lon-span').value = (place.geometry['location'].lng());
document.getElementById('location-snap').value = place.formatted_address; */
/* document.getElementById('lat-span').innerHTML = (place.geometry['location'].lat());
document.getElementById('lon-span').innerHTML = (place.geometry['location'].lng());
document.getElementById('location-snap').innerHTML = place.formatted_address; */
Livewire.emit('getLatitude', place.geometry['location'].lat());
Livewire.emit('getLongitud', place.geometry['location'].lng());
});
/* console.log(searchBox); */
map.fitBounds(bounds);
});
}
That I could try to fix it? Thanks for your time
I solved it by setting the property as follows:
#this.set('latitud', place.geometry['location'].lat());
#this.set('longitud', place.geometry['location'].lng());
Related
Google Places API Web Service has a 1000 request per day quota limit for non-billed accounts. When using the search box autosuggestion feature providing multiple places for each key press, this limit will be reached fairly quickly.
<input id="pac-input" class="controls" type="text" placeholder="Search Box">
<div style="height:100%; width:100%;position:absolute;">
<div id="map"></div>
</div>
<script>
window.onload = initAutocomplete;
function initAutocomplete() {
var my_position = new google.maps.LatLng(51.163375, 10.447683);
var map = new google.maps.Map(document.getElementById('map'), {
center: {lat: 51.163375, lng: 10.447683},
disableDoubleClickZoom: true,
zoom: 9,
mapTypeId: 'roadmap'
});
// Create the search box and link it to the UI element.
var input = document.getElementById('pac-input');
var searchBox = new google.maps.places.SearchBox(input);
map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);
// Bias the SearchBox results towards current map's viewport.
map.addListener('bounds_changed', function() {
searchBox.setBounds(map.getBounds());
});
var markers = [];
var crowdMarker = new google.maps.Marker({
position: my_position,
map: map
});
google.maps.event.addListener(map, 'dblclick', function(e){
var positionDoubleClick = e.latLng;
crowdMarker.setPosition(positionDoubleClick);
var lat = crowdMarker.getPosition().lat();
var lng = crowdMarker.getPosition().lng();
});
// Listen for the event fired when the user selects a prediction and retrieve
// more details for that place.
google.maps.event.addDomListener(searchBox, 'keydown', function (e) {
if (e.keyCode == 13) {
e.preventDefault();
}
});
searchBox.addListener('places_changed', function() {
var places = searchBox.getPlaces();
if (places.length == 0) {
return;
}
// Clear out the old markers.
markers.forEach(function(marker) {
marker.setMap(null);
});
markers = [];
// For each place, get the icon, name and location.
var bounds = new google.maps.LatLngBounds();
places.forEach(function(place) {
if (!place.geometry) {
console.log("Returned place contains no geometry");
return;
}
var icon = {
url: place.icon,
size: new google.maps.Size(71, 71),
origin: new google.maps.Point(0, 0),
anchor: new google.maps.Point(17, 34),
scaledSize: new google.maps.Size(25, 25)
};
// Create a marker for each place.
markers.push(new google.maps.Marker({
map: map,
icon: icon,
title: place.name,
position: place.geometry.location
}));
if (place.geometry.viewport) {
// Only geocodes have viewport.
bounds.union(place.geometry.viewport);
} else {
bounds.extend(place.geometry.location);
}
});
map.fitBounds(bounds);
});
}
</script>
<script src="https://maps.googleapis.com/maps/api/js?key=myAPIKey&libraries=places"
async defer></script>
Things I've tried include setting a bound to the SearchBox to limit the suggestions to a set area, as well as adding a delay within the keydown section.
Can anyone suggest a way to be able to throttle or delay the autosuggestion appearing, and therefore sending fewer requests?
I'm afraid the current implementation of autocomplete and search box in places library of Maps JavaScript API doesn't allow to throttle or delay requests programmatically. You can see a feature request in Google issue tracker for this:
https://issuetracker.google.com/issues/35823678
Please star this feature request to add your vote. The only workaround I can think of is implementing your own autocomplete element that uses a google.maps.places.AutocompleteService class and where you will be able to control a frequency of requests sent to AutocompleteService.
https://developers.google.com/maps/documentation/javascript/reference#AutocompleteService
I am trying to use Google Maps API service to allow my site visitors to type in their address in a box. Once he/she selects a matching address, I want to obtain the latitude and longitude info about the provided address.
Using the code in the API documentation, I got a map and a text box on my site. However, once Google finds a matching address I need to get the latitude and longitude info for the selected address only.
Here is the function that initialize the map
function initMap() {
var mapElement = document.getElementById('map');
var map = new google.maps.Map(mapElement, {
center: {
lat: -34.397,
lng: 150.644
},
zoom: 6
});
// Create the search box and link it to the UI element.
var input = document.getElementById('pac-input');
var searchBox = new google.maps.places.SearchBox(input);
map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);
var infowindow = new google.maps.InfoWindow();
var marker = new google.maps.Marker({
map: map
});
// Bias the SearchBox results towards current map's viewport.
map.addListener('bounds_changed', function() {
searchBox.setBounds(map.getBounds());
});
var markers = [];
// Listen for the event fired when the user selects a prediction and retrieve
// more details for that place.
searchBox.addListener('places_changed', function() {
var places = searchBox.getPlaces();
console.log(places);
if (places.length == 0) {
return;
}
// Clear out the old markers.
markers.forEach(function(marker) {
marker.setMap(null);
});
markers = [];
// For each place, get the icon, name and location.
var bounds = new google.maps.LatLngBounds();
places.forEach(function(place) {
if (!place.geometry) {
console.log("Returned place contains no geometry");
return;
}
/*
var icon = {
url: place.icon,
size: new google.maps.Size(71, 71),
origin: new google.maps.Point(0, 0),
anchor: new google.maps.Point(17, 34),
scaledSize: new google.maps.Size(25, 25)
};
*/
// Create a marker for each place.
markers.push(new google.maps.Marker({
map: map,
//icon: icon,
title: place.name,
position: place.geometry.location
}));
//coords.latitude
var latitude = place.geometry.location.lat();
var longitude = place.geometry.location.lng();
console.log(latitude, longitude);
if (place.geometry.viewport) {
// Only geocodes have viewport.
bounds.union(place.geometry.viewport);
} else {
bounds.extend(place.geometry.location);
}
});
map.fitBounds(bounds);
});
}
The following lines of code gave me the info that I am looking for. However, it seems that it display's it more that once (possibly it is displaying the info for multiple addresses). How can I get the latitude and longitude info only one address?
var latitude = place.geometry.location.lat();
var longitude = place.geometry.location.lng();
I tried adding a click listener where I Can grab the info one the lick event like this
map.addListener("click", function (event) {
var lat = event.latLng.lat();
var lng = event.latLng.lng();
console.log(lat, lng);
});
However, this gives me the latitude and longitude of the place where I click on the map, not the address that I selected from the text box after the autocomplete suggested the matching addresses
I'm sure there is an easier way but I use maps.google.com and right-click wherever I want to find the coordinates for and choose what's here?, then the longitude and latitude will appear at the bottom of the map.
Using the Google's API you can just send an AJAX request, you don't need a map on your page to get coordinates. Check this out!
https://maps.googleapis.com/maps/api/geocode/json?address=1600+Amphitheatre+Parkway,+Mountain+View,+CA&key=YOUR_API_KEY
var responseAsObject;
$("#div").load( "url", function( response, status, xhr ) {
responseAsObject = $.parseJSON(response);
});
var long = responseAsObject.results.geometry.location.lng;
var lat = responseAsObject.results.geometry.location.lat;
I'm trying to pass my api key which is in my application.yml file into a js script tag for google maps, is this possible? If not, what is the best way to handle this? Also, I'm using the Figaro gem to store ENV variables. Thanks in advance.
<% if #location.latitude.present? && #location.longitude.present? %>
<script>
var myLatLng = {lat: <%= #location.latitude %>, lng: <%= #location.longitude %>};
function initAutocomplete() {
var map = new google.maps.Map(document.getElementById('map'), {
center: myLatLng,
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var marker = new google.maps.Marker({
position: myLatLng,
map: map,
title: '<%= #location.name %>'
});
// Create the search box and link it to the UI element.
var input = document.getElementById('pac-input');
var searchBox = new google.maps.places.SearchBox(input);
map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);
// Bias the SearchBox results towards current map's viewport.
map.addListener('bounds_changed', function() {
searchBox.setBounds(map.getBounds());
});
var markers = [];
// Listen for the event fired when the user selects a prediction and retrieve
// more details for that place.
searchBox.addListener('places_changed', function() {
var places = searchBox.getPlaces();
if (places.length == 0) {
return;
}
// Clear out the old markers.
markers.forEach(function(marker) {
marker.setMap(null);
});
markers = [];
// For each place, get the icon, name and location.
var bounds = new google.maps.LatLngBounds();
places.forEach(function(place) {
var icon = {
url: place.icon,
size: new google.maps.Size(71, 71),
origin: new google.maps.Point(0, 0),
anchor: new google.maps.Point(17, 34),
scaledSize: new google.maps.Size(25, 25)
};
// Create a marker for each place.
markers.push(new google.maps.Marker({
map: map,
icon: icon,
title: place.name,
position: place.geometry.location
}));
if (place.geometry.viewport) {
// Only geocodes have viewport.
bounds.union(place.geometry.viewport);
} else {
bounds.extend(place.geometry.location);
}
});
map.fitBounds(bounds);
});
}
</script>
<input id="pac-input" class="controls" type="text" placeholder="Search Box">
<div id="map"></div>
<script src="https://maps.googleapis.com/maps/api/js.erb?key=MAPS_API_KEY&libraries=places&callback=initAutocomplete"
async defer></script
<% end %>
Error
https://maps.googleapis.com/maps/api/js.erb?key=&libraries=places&callback=initAutocomplete
This is what I have and it gives me the error I posted, if I put the Key in directly it works.
<script src="https://maps.googleapis.com/maps/api/js.erb?key=<%= ENV['MAPS_API_KEY'] %>&libraries=places&callback=initAutocomplete"
async defer></script
This worked for me:
<script async defer src=<%="https://maps.googleapis.com/maps/api/js?key=#{ENV['GOOGLE_MAPS_API_KEY']}&callback=initMap"%> type="text/javascript"></script>
You should be able to treat it as any other ENV variable using ERB:
...
<script src="https://maps.googleapis.com/maps/api/js.erb?key=<%=ENV['MAPS_API_KEY']%>&libraries=places&callback=initAutocomplete"
async defer></script>
Try this #{ENV['MAPS_API_KEY']}, I had a similar issue before:
<script src="https://maps.googleapis.com/maps/api/js.erb?key=<%=#{ENV['MAPS_API_KEY']}%>&libraries=places&callback=initAutocomplete" async defer></script>
I guess you just dont wanna put your api_key into a html tag and make your key vulnerable. You can try following code under your index.html, it works for me.
<div id="google">
<script type="text/javascript">
import {
GOOGLE_MAP_API
} from "<the location you save your apikey>";
function changeSrc() {
document.getElementById("google").src = `https://maps.googleapis.com/maps/api/js?key=${GOOGLE_MAP_API}`
}
</script>
</div>
Note that if you're not using Ruby (or Figaro) and you're using Webpack, you can inject it via the HtmlWebpackPlugin.
You can use a data tag to pass env variables
<script id="div" data-key="<%=ENV['MAPS_API_KEY']%>">
const div = document.getElementById("div");
console.log(div.dataset.key);
</script>
Either I am an idiot or this was an egregious oversight on the part of the Google Maps team.
I am attempting to trigger a places search request on a button click event in conjunction with the standard enter keypress event (which is currently working fine). I have combed through the documentation related to the Google Places search box and have found no sutiable solution.
Because of confidentiality reasons I am using the example from the demo.
function initialize() {
var map = new google.maps.Map(document.getElementById('map-canvas'), {
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var defaultBounds = new google.maps.LatLngBounds(
new google.maps.LatLng(-33.8902, 151.1759),
new google.maps.LatLng(-33.8474, 151.2631));
map.fitBounds(defaultBounds);
var input = /** #type {HTMLInputElement} */(document.getElementById('target'));
var searchBox = new google.maps.places.SearchBox(input);
var markers = [];
document.getElementById('button').addEventListener('click', function() {
var places = searchBox.getPlaces();
// places -> undefined
// The assumption is that I could just call getPlaces on searchBox
// but get nothing in 'places'
// Beyond that it doesn't even make a call to the Google Places API
// Currently the only way to perform the search is via pressing enter
// which isn't terribly obvious for the user.
}, false)
google.maps.event.addListener(searchBox, 'places_changed', function() {
var places = searchBox.getPlaces();
for (var i = 0, marker; marker = markers[i]; i++) {
marker.setMap(null);
}
markers = [];
var bounds = new google.maps.LatLngBounds()
for (var i = 0, place; place = places[i]; i++) {
var image = {
url: place.icon,
size: new google.maps.Size(71, 71),
origin: new google.maps.Point(0, 0),
anchor: new google.maps.Point(17, 34),
scaledSize: new google.maps.Size(25, 25)
};
var marker = new google.maps.Marker({
map: map,
icon: image,
title: place.name,
position: place.geometry.location
});
markers.push(marker);
bounds.extend(place.geometry.location);
}
}
You need to first trigger focus on the input element, then trigger a keydown event with code 13 (enter key).
var input = document.getElementById('target');
google.maps.event.trigger(input, 'focus')
google.maps.event.trigger(input, 'keydown', {
keyCode: 13
});
JSFiddle demo
It is more simple than you think. With same starting point as above, https://developers.google.com/maps/documentation/javascript/examples/places-searchbox
Add a button to the HTML
<button id="button">click</button>
in initialize() add this to the very end, before } :
var button = document.getElementById('button');
button.onclick = function() {
input.value='test';
input.focus();
}
input is already defined. All you have to do, to trigger the places search by a button is - really - to focus the input box associated with the searchBox. You dont have to mingle with searchBox or trigger "places_changed" or anything like that, which you can see elsewhere as suggestions - but in fact is not working.
I guess you want to trigger by a button "for some reason", like searching for some specific predifined - thats why I trigger the search with "test", simply by setting the input value to test.
Updated with working demo, based on the google example above -> http://jsfiddle.net/7JTup/
Im working on a google maps plugin (there's always room for another right?) and I'm drawing a preview of the map my users will be inserting into their content. Im able to draw everything I set out to, custom content in the info window, setting the location (through places.Autocomplete) etc. The one thing that is escaping me is custom map icon isn't being drawn.
My goal is to have the default icon drawn on first load, and then update it when it changes
Im not getting any 404 or errors in the console, and I've checked my event handlers and they are all working. Can anyone tell me where I've going astray?
Here is what I have so far:
//Initilize the map
google.maps.event.addDomListener(window, 'load', initialize);
function initialize(infowindow) {
var init_center = new google.maps.LatLng(43.703793, -72.326187);
mapOptions = {
center: init_center,
zoom: parseFloat(mapZoomReturn),
mapTypeId: google.maps.MapTypeId.ROADMAP,
scrollwheel : false,
};
var input = document.getElementById('mapAddress');
var autocomplete = new google.maps.places.Autocomplete(input);
var infowindow = new google.maps.InfoWindow();
//var marker = new google.maps.Marker({
// position: init_center,
// map: map,
// icon: mapMarkerImageReturn
//});
// Draw the map
map = new google.maps.Map(document.getElementById('map_canvas'), mapOptions);
// marker needs to be set after the map
var marker = new google.maps.Marker({
position: init_center,
map: map,
icon: mapMarkerImageReturn
});
// Set up event listeners
// Info window DOM->MAP
google.maps.event.addDomListener(document.getElementById('mapInfoWindow'),
'change', function() {
mapInfoWindowReturn = escape(jQuery('#mapInfoWindow').val());
// get the extra content from feild, by this time the place_changed even will have fired at least once, so we have these values
infowindowPlace = get_info_bubble(locIcon, locName, locAddress, locPhone, locWeb, mapInfoWindowReturn); // returns formatted markup for info bubble
infowindow.setContent(infowindowPlace);
});
// Marker dropdown selection DOM->MAP
google.maps.event.addDomListener(document.getElementById('mapMarker'), 'change', update_maker);
// Custom marker text field DOM->MAP
google.maps.event.addDomListener(document.getElementById('mapMarkerImage'), 'change', update_maker );
function update_maker(){
//update the marker imge - (not working)
markerImage = get_marker_image(); // returns URL as string
marker.setIcon(markerImage);
marker.setPosition(locPlace.geometry.location);
marker.setMap(map);
}
google.maps.event.addListener(autocomplete, 'place_changed', function() {
infowindowPlace = get_info_bubble(locIcon, locName, locAddress, locPhone, locWeb, mapInfoWindowReturn);
infowindow.close();
if (mapMarkerImageReturn !=='' || mapMarkerImageReturn !== false) marker.setVisible(false);
input.className = '';
locPlace = autocomplete.getPlace();
if (!locPlace.geometry) {
// Inform the user that the place was not found and return.
input.className = 'notfound';
return;
}
// If the place has a geometry, then present it on a map.
if (locPlace.geometry.viewport) {
map.fitBounds(locPlace.geometry.viewport);
mapCurrCenter = map.getCenter();
} else {
map.setCenter(locPlace.geometry.location);
map.setZoom(parseFloat(mapZoomReturn));
mapCurrCenter = map.getCenter();
}
// Set the marker image (not working)
markerImage = get_marker_image(); // returns URL as string
marker.setIcon(markerImage);
marker.setPosition(locPlace.geometry.location);
marker.setMap(map);
// get the location values for the info bubble
if (locPlace.address_components) {
//console.log(locPlace.address_components);
// Populate values for info bubble
locName = locPlace.name;
locIcon = locPlace.icon;
locAddress = locPlace.formatted_address;
locPhone = locPlace.formatted_phone_number;
locWeb = locPlace.website;
}
infowindowPlace = get_info_bubble(locIcon, locName, locAddress, locPhone, locWeb, mapInfoWindowReturn);
infowindow.setContent(infowindowPlace);
infowindow.open(map, marker);
});
}