I am loading a bunch of markers based on a dataset that I am reading from Mongodb in my ejs file. I just loop through the markers to add them to the map and then a "click" eventlistener to display some comment for each marker in the infowindow:
var infowindow = new google.maps.InfoWindow();
var marker, i;
<%for (i = 0; i < <%=comments.length%>; i++) {%>
var LatLng = {lat: <%=comments[i].lat, lng: <%=comments[i].lng%>};
var marker = new google.maps.Marker({
position: LatLng,
map:map,
title: 'Click to see comment details',
id:'<%=comments[i]._id%>'
});
marker.addListener('click', (function(marker, i) {
return function() {
var contentString = `
<div class="comment-item">
<div id="bodyContent">
<p class="lead mt-2"><%=comments[i].textComment%></p>
</div>
</div>
<div>
<form style="display: inline" method="POST" action="/admin/comments/<%=comments[i]._id%>" class="approve-comment-form">
<input type="submit" value="Approve Comment" class="btn btn-success"/>
</form>
</div>
`
infowindow.setContent(contentString);
infowindow.open(map, marker);
}
})(marker, i));
<%}%>
What I am trying to do is to submit the form (approve the comment) using ajax code below. This basically, updates a field for that comment in my Mongodb document.
$(document).on('submit','.approve-comment-form', function(event){
event.preventDefault();
var editCommentData = $(this).serialize();
var editCommentAction = $(this).attr('action');
var $originalCommentItem = $(this).parent('.comment-item');
$.ajax({
url: editCommentAction,
data: editCommentData,
type: 'PUT',
originalItem: $originalCommentItem,
success: function(data) {
console.log(data)
}
});
})
So, the form submission works well and if I refresh the page, that marker will not be displayed on the map (Because the "ajax put" method toggles a switch from on to off for that marker and on page reload it won't be displayed), but the problem is that I want the marker to hide also with the form submit (Without the page refresh) so that I know I have already approved the comment inside it.
Related
I'm fairly new to the Google Maps API and have to use it to locate some addresses. I want to know how the Google Maps marker can be located with the address written in a text field. I've been reading Google's documentation on this, but it hasn't been very helpful.. So far I have managed to make the marker move and update the latitude and longitude values according to the end of the marker movement.
In the html I have this:
<!-- Address input -->
<div class="col-md-3">
<div class="form-group">
<label for="example-text-input" class="form-control-label">Address</label>
<input class="form-control" type="text" name="address" id="address">
</div>
</div>
<!-- shows the current latitude -->
<div class="col-md-3">
<div class="form-group">
<label for="example-text-input" class="form-control-label">Lat</label>
<input class="form-control" type="text" name="latitude" id="lat">
</div>
</div>
<!-- shows the current longitude -->
<div class="col-md-3">
<div class="form-group">
<label for="example-text-input" class="form-control-label">Lng</label>
<input class="form-control" type="text" name="longitude" id="lng">
</div>
</div>
<!-- show the map -->
<div class="row">
<div class="col">
<div class="card border-0">
<div id="map-default" class="map-canvas" style="height: 600px;"></div>
</div>
</div>
</div>
On the js I have this:
var $map = $('#map-default'),
map,
lat,
lng,
color = "#5e72e4";
function initMap() {
map = document.getElementById('map-default');
map = new google.maps.Map(document.getElementById('map-default'), {
zoom: 12,
scrollwheel: true,
center: new google.maps.LatLng(40.71427 , -74.00597),
mapTypeId: google.maps.MapTypeId.ROADMAP,
});
var marker = new google.maps.Marker({
position: new google.maps.LatLng(4.60971, -74.08175),
map: map,
animation: google.maps.Animation.DROP,
// title: 'Marcador',
draggable: true
});
var infowindow = new google.maps.InfoWindow({
content: String(marker.getPosition())
});
google.maps.event.addListener(marker, 'click', function(evt) {
infowindow.open(map, marker);
});
google.maps.event.addListener(marker, 'dragend', function(evt){
$("#lat").val(evt.latLng.lat().toFixed(6));
$("#lng").val(evt.latLng.lng().toFixed(6));
map.panTo(evt.latLng);
});
map.setCenter(marker.position);
marker.setMap(map);
}
if($map.length) {
google.maps.event.addDomListener(window, 'load', initMap);
}
To find an address for the marker ( whether that is based upon a click event, drag event or static lat/lng ) you will need to use the Geocoder api - note however that this has a quota limit and contributes to the maps usage statistics! [ see here for details ]
The process is known as reverse geocoding - the geocoder is supplied with a lat/lng value and will return a JSON response with details of the chosen location, from which you can extract the address.
Based upon your original code but without any jQuery
<script>
function initMap(){
const map = new google.maps.Map(document.getElementById('map-default'), {
zoom: 12,
scrollwheel: true,
center: new google.maps.LatLng( 40.71427 , -74.00597 ),
mapTypeId: google.maps.MapTypeId.ROADMAP,
});
// initialise the GeoCoder
const geocoder = new google.maps.Geocoder();
const infowindow = new google.maps.InfoWindow();
// utility to populate the input elements with discovered values
const populate=( lat, lng, addr )=>{
document.querySelector('input[name="address"]').value=addr;
document.querySelector('input[name="latitude"]').value=lat.toFixed(6);
document.querySelector('input[name="longitude"]').value=lng.toFixed(6);
};
// function to invoke the geocoder and find the address from given latlng
const findlocation=(e)=>{
geocoder.geocode( { 'location':e.latLng }, ( results, status )=>{
return evtcallback( results, status, e.latLng);
});
};
// respond to clicks on the map
const mapclickhandler=function(e){
findlocation.call( this, e );
};
// respond to clicks on the marker
const markerclickhandler=function(e){
infowindow.open( map, this );
infowindow.setContent( e.latLng.lat()+', '+e.latLng.lng() );
if( this.hasOwnProperty('address') )infowindow.setContent( this.address );
};
// respond to drag events of the marker.
const draghandler=function(e){
findlocation.call( this, e );
map.panTo( e.latLng );
};
// callback function that analyses the Geocoder response
const evtcallback=function( results, status ){
let _address, _location;
if( status == google.maps.GeocoderStatus.OK ){
_address=results[0].formatted_address;
_location=results[0].geometry.location;
// set custom properties for the marker
// which are used to populate infowindow
marker.address=_address;
marker.location=_location;
// populate the HTML form elements
populate( _location.lat(), _location.lng(), _address );
// open and set content for the infowindow
infowindow.open( map, marker );
infowindow.setContent( _address );
latlng=new google.maps.LatLng( _location.lat(), _location.lng() );
marker.setPosition( latlng );
map.setCenter( latlng );
map.setZoom( 15 );
return true;
}
console.warn( status );
};
let marker = new google.maps.Marker({
position: new google.maps.LatLng( 4.60971, -74.08175 ),
map: map,
animation: google.maps.Animation.DROP,
draggable: true
});
google.maps.event.addListener( map,'click', mapclickhandler );
google.maps.event.addListener( marker, 'click', markerclickhandler );
google.maps.event.addListener( marker, 'dragend', draghandler );
map.setCenter( marker.position );
}
</script>
<script async defer src="https://maps.googleapis.com/maps/api/js?key=APIKEY&callback=initMap"></script>
i have this button and onclick i want to pass lat lng to initialize function for google map:
<button class="btn btn-block btn-primary" onclick="initialize()" id='latlng' lat="{{$data['clients_address']['lat']}}" lng="{{$data['clients_address']['lng']}}">
Map Location
</button>
i tried passing with this event but its not working and if with jquery then how can i pass to javascript function
$('#latlng').click(function(e){
var lng = $(this).attr('lng');
alert(lng,lat);
});
<script>
function initialize() {
//console.log(this.getAttribute('lat'));
var latlng = new google.maps.LatLng(28.535516,77.391026);
var map = new google.maps.Map(document.getElementById('map'), {
center: latlng,
zoom: 13
});
var marker = new google.maps.Marker({
map: map,
position: latlng,
draggable: false,
anchorPoint: new google.maps.Point(0, -29)
});
var infowindow = new google.maps.InfoWindow();
google.maps.event.addListener(marker, 'click', function() {
var iwContent = '<div id="iw_container">' +
'<div class="iw_title"><b>Location</b> : Noida</div></div>';
// including content to the infowindow
infowindow.setContent(iwContent);
// opening the infowindow in the current map and at the current marker location
infowindow.open(map, marker);
});
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
In your example, you first used a .click to an ID selector. If you really only have one button that calls initialize() on the page you can pass PHP variables directly to Javascript:
<script>
let lat = <?php echo json_encode($data['clients_address']['lng']); ?>;
let lng = <?php echo json_encode($data['clients_address']['lng']); ?>;
function initialize() {
...
}
</script>
If you have multiple initialize() triggers, you can use #Mahmoud answer or pass the values as function parameters.
<button class="btn btn-block btn-primary" onclick="initialize({{$data['clients_address']['lat']}}, {{$data['clients_address']['lng']}})" id="latlng">
Map Location
</button>
function initialize(lat, long) {
...
}
Personally I tend to use this + element attributes when I need to reference a standard attribute (ex: value, class) or the value is referenced in more than one place (ex: not only in initialize function). Also, consider using jQuery data method if you intend to use attributes.
Att,
First you need to pass the clicked element to the initialize like so
<button class="btn btn-block btn-primary" onclick="initialize(this)" id='latlng' lat="{{$data['clients_address']['lat']}}" lng="{{$data['clients_address']['lng']}}">
Map Location
</button>
then in your initialize function you will get the lat and lng
function initialize(element) {
var lat = $(element).attr('lat');
var lng = $(element).attr('lng');
.....
}
Edit: I solved the problem. Leaving it up in case someone needs something similar in the future.
I am trying to use google map api to get coordinates and save the information in a database. If the user drags the marker then a form is updated with the information. User can also enter the information manually in the form if she wants and the map will be updated with a new marker. Then the user can click submit and the information will be saved in the database.
<form action="{{ route('YOUR_ROUTE') }}" method="post" enctype="multipart/form-data">
<div class="form-group">
<div style="padding-top: 10px;">
<div id="map-location"></div>
</div>
{{-- visibility, lat, lng, location --}}
<input class="form-control" type="text" name="location_name" id="location_name" placeholder="Location">
<input class="form-control" type="text" name="latitude" id="latitude" placeholder="Latitude" style="max-width: 50%;">
<input class="form-control" type="text" name="longitude" id="longitude" placeholder="Longitude" style="max-width: 50%;">
<button type="submit" class="btn btn-primary">Post</button>
<input type="hidden" value="{{ Session::token() }}" name="_token">
</div>
</form>
<script>
//initialise and add the map
function initMap(){
// location of midtown manhattan
var midtown = {lat: 40.7549, lng: -73.9840};
// the maps centered at midtown
var map = new google.maps.Map(document.getElementById('map-location'),{zoom:17, center:midtown});
// the marker
var marker = new google.maps.Marker({position: midtown, map: map, draggable: true});
infoWindow = new google.maps.InfoWindow;
// Try HTML5 geolocation.
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
var pos = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
marker.setPosition(pos);
map.setCenter(pos);
}, function() {
handleLocationError(true, infoWindow, map.getCenter());
});
} else {
// Browser doesn't support Geolocation
handleLocationError(false, infoWindow, map.getCenter());
}
function handleLocationError(browserHasGeolocation, infoWindow, pos) {
infoWindow.setPosition(pos);
infoWindow.setContent(browserHasGeolocation ?
'Error: The Geolocation service failed.' :
'Error: Your browser doesn\'t support geolocation.');
infoWindow.open(map);
}
// dragged event of the marker
google.maps.event.addListener(marker, 'dragend', function(){
var lat = marker.getPosition().lat();
var lng = marker.getPosition().lng();
var address;
$('#latitude').val(lat);
$('#longitude').val(lng);
var geocoder = new google.maps.Geocoder();
geocoder.geocode({latLng: marker.getPosition()}, function(result,status){
if(status == google.maps.GeocoderStatus.OK){
address = result[0].formatted_address;
// console.log(address);
$('#location_name').val(address);
}
else{
console.log('Geocode was not successful for the following reason: ' + status);
}
});
});
// when the place is changed in the location box the map updates
searchBox = new google.maps.places.SearchBox(document.querySelector( '#location_name' ));
google.maps.event.addListener(searchBox, 'places_changed', function(){
var places = searchBox.getPlaces(),
bounds = new google.maps.LatLngBounds();
for(var i = 0; place = places[i]; i++){
bounds.extend(place.geometry.location);
marker.setPosition(place.geometry.location);
}
map.fitBounds(bounds);
map.setZoom(15);
var lat = marker.getPosition().lat();
var lng = marker.getPosition().lng();
$('#latitude').val(lat);
$('#longitude').val(lng);
});
}
</script>
<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_KEY&libraries=places&callback=initMap"></script>
I updated my first post with the code that is working for me.
I couldn't update the map with the information from the form because I didn't include the places library while loading the Maps JavaScript API.
I couldn't see the updated coordinates after dragging the map marker because I didn't enable the geocoding api and didn't create a billing account with google cloud. I also restricted my API key.
Hope this helps.
i am using google places details api. So in my src attribute I put google api which has a callback named initMap
Here is the code
<div class="tab-pane active" id="timeline">
<p class="lead">Location</p>
<hr>
<div class="row">
<div class="col-md-1"></div>
<div class="col-md-8">
<h2>
<span>location <b style="color:black;"> kolkata</b></span></h2>
<p></p>
<div id="map" style="width:100%;height:400px;"></div>
<script src="https://maps.googleapis.com/maps/api/js?key=API_KEY&libraries=places&callback=initMap"></script>
<p></p>
</div>
</div>
</div>
In same html I have written initMap function
<script>
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
center: {lat: 22.652687, lng: 88.376882},
zoom: 15
});
var infowindow = new google.maps.InfoWindow();
var service = new google.maps.places.PlacesService(map);
service.getDetails({
placeId: 'ChIJvYbRT7Gd-DkR6QZQfJP9ZJg'
}, function(place, status) {
debugger
if (status === google.maps.places.PlacesServiceStatus.OK) {
var marker = new google.maps.Marker({
map: map,
position: place.geometry.location
});
google.maps.event.addListener(marker, 'click', function() {
infowindow.setContent('<div><strong>' + place.name + '</strong><br>' +
'Place ID: ' + place.place_id + '<br>' +
place.formatted_address + '</div>');
infowindow.open(map, this);
});
}
});
}
</script>
This works perfectly as long as this function is present inside script tag.
How do I call initMap inside controller?
You can't call it that way but instead you will load the script using javascript inside your controller when your app is loaded using document.createElement('script') then hook up a onload event listener then append it inside head head tag.
most likely:
var script = document.createElement('script');
script.src = 'google api script';
script.onload = function () {
// your onload function
$scope.onload();
$scope.$digest();
};
document.querySelector('head').appendChild(script);
this way you have control over what ng stuff you want to do everytime it loads. hope that helps
When inside your HTML page, you create a function, the function will be automatically assigned to the window object, because that's the default scope of the scripts included in the page.
So from your controller, you should be able to simply call the function just using: window.initMap();
I'm creating an app that renders a Google Map of a city, with location markers for nearby locations. I'm rendering these locations by requesting locations within an 800-unit radius to the google.maps.places.PlacesService API. In addition, I have a number of checkboxes representing the place types (e.g. park, museum, etc.). When these boxes are checked or unchecked, I'd like for the map to dynamically update and show or hide the corresponding map markers. I'm using the knockout.js framework to help with the implementation.
My checkboxes are knockout observables, and I've managed to create an array of the checkboxes that changes dynamically within the html. However, even though the array changes, the map itself does not, even though I have set the types variable to be my location that stores the results from the checkbox selection (data.Locations.arrLocations). How do I go about getting the correct bindings in this case?
I've included my index.html and app.js files below:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<style>
html, body {
height: 100%;
margin: 0;
padding: 0;
}
#map {
height: 100%;
}
</style>
</head>
<body>
<!-- This is a *view* - HTML markup that defines the appearance of your UI -->
<div id='searchBar'>
<p>Search: <strong data-bind="text: location"></strong></p>
<p><input type='checkbox' data-bind='checked: Locations' value='gym'> Gyms </p>
<p> <input type='checkbox' data-bind='checked: Locations' value='park'>Parks </p>
<p> <input type='checkbox' data-bind='checked: Locations' value='store'> Stores </p>
<p> <input type='checkbox' data-bind='checked: Locations' value='museum'> Museums </p>
<p> <input type='checkbox' data-bind='checked: Locations' value='zoo'> Zoos </p>
<p>Search: <input data-bind="value: location" /></p>
<div data-bind='text: ko.toJSON($data.Locations)'></div>
</div>
<div id='map'></div>
<div id="map"></div>
<script src="https://maps.googleapis.com/maps/api/js?signed_in=true&libraries=places&callback=initMap" async defer></script>
<script type='text/javascript' src='https://cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-min.js'></script>
<script type='text/javascript' src='js/app.js'></script>
</body>
</html>
app.js:
var data = {
'Locations' : {
'arrLocations': ['park', 'gym']
}
};
function AppViewModel() {
this.location = ko.observable("Barcelona, Spain");
this.Locations = ko.observableArray(data.Locations.arrLocations);
}
var map;
var infowindow;
function initMap() {
var barcelona = {lat: 41.383, lng: 2.183};
map = new google.maps.Map(document.getElementById('map'), {
center: barcelona,
zoom: 15
});
infowindow = new google.maps.InfoWindow();
var service = new google.maps.places.PlacesService(map);
service.nearbySearch({
location: barcelona,
radius: 800,
types: data.Locations.arrLocations,
}, callback);
}
function callback(results, status) {
if (status === google.maps.places.PlacesServiceStatus.OK) {
for (var i = 0; i < results.length; i++) {
createMarker(results[i]);
}
}
}
function createMarker(place) {
var placeLoc = place.geometry.location;
var marker = new google.maps.Marker({
map: map,
position: place.geometry.location
});
google.maps.event.addListener(marker, 'click', function() {
infowindow.setContent(place.name);
infowindow.open(map, this);
});
}
// Activates knockout.js
ko.applyBindings(new AppViewModel());
You need to subscribe to changes in Locations and adjust your markers accordingly. To clear unwanted markers, you need to keep track of what markers you have added. I did this with a markers array. Each time Locations changes, all markers are removed, and then the search runs and adds markers.
viewModel.Locations.subscribe(function (newValue) {
console.debug("Changing", newValue);
for (var i=0; i<markers.length; ++i) {
markers[i].setMap(null);
}
markers = [];
service.nearbySearch({
location: barcelona,
radius: 800,
types: newValue
}, callback);
});
viewModel.Locations.notifySubscribers();
The notifySubscribers call causes the subscription to fire for the first run.
http://jsfiddle.net/53qfm5bz/1/