Embed a Google Map into a dynamically created div - javascript

I'm trying to dynamically generate a Google map when the user submits a form, but the map does not appear in its div when the form is submitted.
I tested to see if the map populates on merely on pageload and it does, but when trying to use a div that displays onclick the map does not show.
<form onsubmit="return false" action="">
<input type="text" id="addLocation"/>
<button onclick="findLocation()" id="btnLocation">Find Location</button>
</form>
<div id="mapContainer"></div>
This is the JavaScript:
function findLocation(){
var inputString = $('#addLocation').val();
$('#mapContainer').html('<div id="mapCanvas"></div>');
var apiRequest = $.ajax({
url: 'http://xxxxx.net/json/'+ inputString,
dataType: 'json',
type: 'get',
});
apiRequest.done(function(data){
var lat = data['latitude'];
var lng = data['longitude'];
function initialize() {
var mapOptions = {
center: new google.maps.LatLng(lat, lng),
zoom: 8
};
map = new google.maps.Map(document.getElementById('mapCanvas'), mapOptions);
}
// from GMaps API docs
function loadScript() {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'https://maps.googleapis.com/maps/api/js?v=3.exp' + '&callback=initialize';
document.body.appendChild(script);
}
window.onload = loadScript;
}); // ends .done statement
} // ends findLocation
It should be that when you click the Find Location button it will generate a map in the mapCanvas div but it does not.
I found out from the Google Maps API documentation that if you want to asynchronously load a map you have to use that loadScript function.
And yes, the map divs have widths and heights in the CSS, so that's not the problem. I think the problem is with the Javascript scopes. I'm doing something wrong with the order or placement of the calls, just not sure what.

First, it is not true that you have to load the Maps API asynchronously when you want to create a map dynamically. You can load the API either using the asynchronous method or with a conventional <script> tag. Loading the API does not create a map; it merely loads the code. A map is not created until you call new google.maps.Map().
So one easy solution would be to go back to a <script> tag to load the API, and simply call your initialize() function directly inside your ajax callback. (Or pull the code out of the initialize() function and just run it inline inside the callback.)
In fact, in your current code you are already loading the Maps API at page load time, with this line:
window.onload = loadScript;
That causes the loadScript() function to be called at page load time.
So you may as well just use a <script> tag to load the API, it's more or less the same thing you're doing now but simpler.
If you do want to load the Maps API dynamically, #sirfriday's comment is correct: your initialize function is not visible in the global scope, and it needs to be so the asynchronous API load can call it when ready.
You mentioned that "calling initialize didn't do it." - but you shouldn't be calling initialize() in this case, you should set window.initialize to be a reference to the function, e.g.:
window.initialize = initialize; // note no () so it doesn't call it here
Or change the function like this:
window.initialize = function() {
...
};
And also if you want to load the API dynamically, don't do it at onload time. Instead, call your loadScript() function inside your ajax callback. That will give you the right sequence of operations:
Page loads
User interacts with it and the ajax request starts
Ajax request completes
Maps API loads
Your initialize() function gets called

Related

Map always returns undefined

I've been trying to display Bing Map whenever a user clicks on a certain HTML element, but Microsoft.Maps.Map always returns undefined even after setting a timeInterval, I have also tried inserting a new scripts with onscriptload onclick event.
Below are my sample code.
$(document).ready(function(){
var script = document.createElement("script");
script.type = 'text/javascript';
script.src = 'https://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0&onscriptload=getMap';
document.head.appendChild(script);
})
$("#showMap").click(function(){
getMap();
})
function getMap(){
console.info(Microsoft.Maps.Map);
var time = "";
var map
var mapControl = Microsoft.Maps.Map;
if(mapControl == undefined){
time = setInterval(function(){ getMap() }, 8000);
}
else{
map = new Microsoft.Maps.Map(document.getElementById('address1_composite'), {credentials: 'Cred'});
clearInterval(time);
}
}
Sorry I know this question is already asked here in this link, Bing map is undefined after loading Javascript file
We have the same scenario but mine is not working as expected though i have tried the suggested answer.
What you should do instead of creating the map when an element is clicked is load the map once and then hide/show it when the button is clicked. This will significantly reduce the number of map loads which means less transactions are generated and thus lower costs.
Also, instead of loading the map script on demand like you are, use the Bing Maps V8 control and load it asynchronously: https://msdn.microsoft.com/en-us/library/mt712557.aspx

Enabling Google maps after data has been switched on

I have a Cordova app with an angular bootstrap kicking in on 'deviceready'.
I have Google maps linked like so:
<script src="https://maps.googleapis.com/maps/api/js"></script>
If the app starts with data set to off then obviously the script will be missing.
If the user turns on data how can i initialise Google maps? Must i restart the app? I've tried injecting the script via 'document' but the error message said that's not allowed.
Any help or tips would be very much welcomed.
Cheers
Dale
Basically this code will check if google maps api libraries are loaded when building a map within your angular controller and try get it if not.
//already loaded build the map
if ($window.google && $window.google.maps) {
prepareMap();
} else { //not loaded
//create a promise object
var mapScriptPromise = getMapScript();
mapScriptPromise.then(function(){
prepareMap(); //now you can build the map
});
}
//create the hook for the file and return the deferred promise
function getMapScript() {
var deferred = $q.defer();
var script = document.createElement('script');
//script callback
$window.initMap = function() {
deferred.resolve();
}
script.src = "https://maps.googleapis.com/maps/api/js?sensor=true&callback=initMap";
document.body.appendChild(script);
return deferred.promise;
}
//add your map settings here i.e.
function prepareMap() {
var map = new google.maps.Map(mapCanvas, mapOptions);
...
}

Google.maps callback appears to run before the api is completely loaded

I want to load the maps.googleapis if the user clicks on a view that contains a map, so I have code to load the api with a callback parameter:
<script type="text/javascript">
$('div[data-role="page"]').bind('pagecreate', function () {
var script = document.createElement("script");
script.id = "addedformaps";
script.type = "text/javascript";
script.src = "http://maps.googleapis.com/maps/api/js?sensor=false&callback=initMap";
document.body.appendChild(script);
});
The initMap function references item in the google.maps object, such as:
<script type="text/javascript">
function initMap() {
var mapType = google.maps.MapTypeId.ROADMAP;
The first time the view is loaded, I get an error because google.maps.MapTypeId is undefined. Subsequent loads work, until the browser cach is cleared, and then the error occurs again. I am able to get around this by adding a SetTimeout to the initMap function:
<script type="text/javascript">
setTimeout(function initMap() {
...
}, 500)
</script>
but I am not comfortable with slowing down every load. Why is the callback function executing before the google.maps object is fully loaded?

Opening a second InfoWindow in Google Maps API v3

I'm implementing a Maps interface for a database of music venues and events and have run into an interesting issue. I have a series of HTML elements with onclick calls to a certain Javascript function. The calls run correctly and the javascript function runs correctly the first time (all the information is passed in correctly and my debug alerts display it correctly), and the infoWindow displays. The second time I click on one of these divs, the first one closes correctly, the final alert in the code below fires with the correct information, but the new InfoWindow does not pop up.
Map setup code:
function mapsInitialize() {
var mapOptions = {
center: new google.maps.LatLng(42.4439614, -76.5018807),
zoom: 14,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("mapSection"), mapOptions);
google.maps.event.trigger(map, 'resize');
for(var x = 0; x < markers.length; x++){
var tempOptions = {
position: markers[x].position,
title: markers[x].name,
map: map
}
markerObjects[markers[x].title] = new google.maps.Marker(tempOptions);
}
}
Function called onclick from the divs:
function generateInfoWindow(despacedName, despacedTitle, eventTitle, url, date, time){
if(curInfoWindow){
alert("closing " + curInfoWindow.getContent());
curInfoWindow.close();
curInfoWindow = null;
}
curInfoWindow = new google.maps.InfoWindow(options = {size: new google.maps.Size(150,50)});
curInfoWindow.setContent("<a class=\"eventLink\" href=\""+url+"\">"+eventTitle+"</a><br><br>"+markerObjects[despacedName].title+"<br>"+date+" at "+time);
curInfoWindow.open(map, markerObjects[despacedName]);
alert(despacedName+" is "+markerObjects[despacedName]);
}
I can guarantee that the markers[] array is fed correctly.
I have tried, among other things...
Creating an array to hold the infoWindows instead of using one curInfoWindow variable
Not having anything in the array automatically close like the beginning of the generateInfoWindow() function does
Creating the info windows automatically in the mapsInitialize() function
Most of the results on Google when searching for solutions brought me information about event listeners on the map - is that the only way to fire an event like this, or is what I'm trying to do valid?
If any other code examples are needed, let me know. Thanks for any suggestions!
According to Google Maps API:
InfoWindows may be attached to either Marker objects (in which case
their position is based on the marker's location) or on the map itself
at a specified LatLng. If you only want one info window to display at
a time (as is the behavior on Google Maps), you need only create one
info window, which you can reassign to different locations or markers
upon map events (such as user clicks). Unlike behavior in V2 of the
Google Maps API, however, a map may now display multiple InfoWindow
objects if you so choose.
Maybe this would work:
function generateInfoWindow(despacedName, despacedTitle, eventTitle, url, date, time){
if(!curInfoWindow){
curInfoWindow = new google.maps.InfoWindow({maxWidth:150});
}
curInfoWindow.setContent("<a class=\"eventLink\" href=\""+url+"\">"+eventTitle+"</a><br><br>"+markerObjects[despacedName].title+"<br>"+date+" at "+time);
curInfoWindow.open(map, markerObjects[despacedName]);
alert(despacedName+" is "+markerObjects[despacedName]);
}
According to the specs, this should move the existing curInfoWindow to a new marker and update the content.
Note: this code is invalid
new google.maps.InfoWindow(options = {size: new google.maps.Size(150,50)})
There is no size attribute. The best you can do is
new google.maps.InfoWindow({maxWidth:150})
More about InfoWindow API

Running a JS Method on load from code behind

I am trying to load the following JS method:
<script type="text/javascript">
function initialize(lon,lat) {
var myLatlng = new google.maps.LatLng(lon,lan);
var myOptions = {
zoom: 14,
center: myLatlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
var map = new google.maps.Map(document.getElementById("map"), myOptions);
var marker = new google.maps.Marker({
position: myLatlng,
map: map
});
}
</script>
In my code behind in VB.NET I then want to load such as:
initialize(53.349803617967446, -6.260257065296173)
I need to load in code behind since I have to get the longtidude and latidude from DB.
An alternative to Chuck's suggestion - you could keep the function in the .aspx and write out those values directly into the Javascript using inline code, like the following:
initialize(<%=strLat%>, <%=strLon%>)
strLat and strLon would be defined as protected variables in your code-behind.
If you really want the whole function to be written out by the code-behind, look into the RegisterClientScriptBlock method.
It's not usually best practice to call Javascript functions directly from the code behind. The code behind is code that happens prior to the page's rendering.
What I would do is add two hidden literals to your page, latitude and longitude, and call your initialize function on the DOM ready event. The initialize function would retrieve the lat/long values from the created literals.
Here is a good resource to learn about the DOM ready event if you're not familiar.
Edit: If the above is not an option, you can "call" it from the code-behind in a rather roundabout way (This is almost functionally equivalent to my other solution).
<body runat="server">
Make your body tag look like that and then add this to the code-behind:
var javascriptFunction = string.format("javascript:initialize({0}, {1})", latValue, longValue);
Body.Attributes.Add("Onload", javascriptFunction);
You can use RegisterClientScriptBlock to insert Javascript into the page from the code-behind.
You would insert it inside whatever function you want to register it from, and format it like this:
ClientScriptManager.RegisterClientScriptBlock(typeof(this), "SomeKeyName", "initialize(" + lat + "," + long + ");", true);
where SomeKeyName is a unique key that prevents the script from being added more than once per postback.
If you must register both the function and the call from the code-behind, you would want to use ScriptManager.RegisterClientScriptBlock for the function, and ScriptManager.RegisterStartupScript for the call to the function. If the rendering of the function is not dependent upon anything from your server code, you would be better off not registering it from code, though.

Categories

Resources