TypeError: window.initMap is not a function - angularjs

I am following this tutorial, basically copy all the code
https://developers.google.com/maps/documentation/javascript/tutorial
but got an error saying that the initMap function is not a function.
I am using angularjs in my project, could that be causing problems?
I copied the same code into plunker and it works just fine...
What are the possible issues?

Actually the error is being generated by the initMap in the Google's api script
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap">
</script>
so basically when the Google Map API is loaded then the initMap function is executed.
If you don't have an initMap function, then the initMap is not a function error is generated.
So basically what you have to do is one of the following options:
to create an initMap function
replace the callback function with one of your own that created for the same purpose but named it something else
replace the &callback=angular.noop if you just want an empty function() (only if you use angular)

My explanation of this problem:
The .....&callback=initMap" async defer></script> makes that script load async (in parallel with DOM) and after load - execute initMap function. NOTICE that initMap() in global scope! We can't declare our initMap() after google's script because it should already be at the moment of finish async load. Although we can't load our script before this, because we need google's function to execute ours. This is like vicious circle.
Three solutions:
1'st and worst: make google's script load synchronously
remove async defer
remove &callback=initMap in src attribute
put <script tag with your code after google's script
2'nd and best: just do this =)
leave .....&callback=initMap" async defer></script> as is
and put google's <script tag after your script
write in your script
function initMap() {} // now it IS a function and it is in global
$(() => {
initMap = function() {
// your code like...
var map = new google.maps.Map(document.getElementById('map'), {/*your code*/});
// and other stuff...
}
})
this allow you load google's script async and run yours just after that
3'rd and strange: it will work... some times =)
do same but simply write in global scope
function initMap() {
// your code
}
and if you write it in global scope, that will work regardless of what
which code loads faster, your (sync) or google's (async). More often your wins

I have been struggling for several days with this very popular in the last few months issue - "initMap is not a function".
Those two threads helped me:
How to make a callback to Google Maps init in separate files of a web app
Defer attribute doesn't work with Google Maps API?
Why does the map open sometimes and sometimes not. It depends on several factors like speed of connection, environment, etc. Because the initialization function sometimes runs after the google maps API kicks in, that's why the map is not displayed and the browser console throws an error. For me removing only the async attribute fixed the issue. The defer attribute stays.
If async is present: The script is executed asynchronously with the rest of the page (the script will be executed while the page continues the parsing)
If async is not present and defer is present: The script is executed when the page has finished parsing
If neither async or defer is present: The script is fetched and executed immediately, before the browser continues parsing the page
Source - http://www.w3schools.com/tags/att_script_defer.asp
Hope that helps. Cheers.

Removing =initMap worked for me:
<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback"></script>

The problem has to do with the async attribute in the script tag. The Callback Function is trying to call "initMap()" when it doesn't really exists by the time the request finished.
To solve this I placed the Goole Maps Api Script bellow the script where my initMap function was declared.
Hope this helps

This may seem obvious but just in case: If someone placed the JS code inside $(document).ready like this:
$(document).ready(function() {
... Google Maps JS code ...
}
Then that's the problem because using async defer when loading the Google Maps API library, it will load asynchronously, and when it finishes loading, will look for the callback function, which needs to be available by then.
So, you just need to put the code outside $(document).ready, and:
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"
async defer></script>
at the very bottom, so your page loads FAST :-)

Solved by adding
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=XXXXXXX&callback=initMap">
<!--
https://developers.google.com/maps/documentation/javascript/examples/map-geolocation
-->
</script>
At the beginning of the same file which contains the rest of the code with function initMap(). It's definitely not the best solution, but it works..
But I think that if you would transform function initMap() to something like var=initMap() and then $(function () ... it would work too.

I am using React and I had mentioned &callback=initMap in the script as below
<script
src="https://maps.googleapis.com/maps/api/js?
key=YOUR_API_KEY&callback=initMap">
</script>
then is just removed the &callback=initMap part now there is no such error as window.initMap is not a function in the console.

your call back method probably is not globally accessible. in my case I'd used transpoiled ES6 codes via webpack which caused my callback method not being global anymore.
Try to attach your callback method explicitly to window like so right after your callback method declaration and see the result
window.initMap = initMap;
it worked for me.

Create initMap method between "" tag or load javascript file before call google api.
<script src="Scripts/main.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?key=abcde&libraries=places&callback=initMap" async defer></script>

For me the main difference was the declaration of the function....
INSTEAD OF
function initMap() {
...
}
THIS WORKED
window.initMap = function () {
...
}

Put this in your html body (taken from the official angular.js website):
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
I believe that you were using some older angular.js files since you don't have any issues in plunker and therefore you got the specified error.

turns out it has to do with ng-Route and the order of loading script
wrote a directive and put the API script on top of everything works.

I had a similar error. The answers here helped me figure out what to do.
index.html
<!--The div element for the map -->
<div id="map"></div>
<!--The link to external javascript file that has initMap() function-->
<script src="main.js">
<!--Google api, this calls initMap() function-->
<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEYWY&callback=initMap">
</script>
main.js
// This gives error
// The initMap function has not been executed
const initMap = () => {
const mapDisplayElement = document.getElementById('map');
// The address is Uluru
const address = {lat: -25.344, lng: 131.036};
// The zoom property specifies the zoom level for the map. Zoom: 0 is the lowest zoom,and displays the entire earth.
const map = new google.maps.Map(mapDisplayElement, { zoom: 4, center: address });
const marker = new google.maps.Marker({ position: address, map });
};
The answers here helped me figure out a solution.
I used an immediately invoked the function (IIFE ) to work around it.
The error is as at the time of calling the google maps api the initMap() function has not executed.
main.js
// This works
const mapDisplayElement = document.getElementById('map');
// The address is Uluru
// Run the initMap() function imidiately,
(initMap = () => {
const address = {lat: -25.344, lng: 131.036};
// The zoom property specifies the zoom level for the map. Zoom: 0 is the lowest zoom,and displays the entire earth.
const map = new google.maps.Map(mapDisplayElement, { zoom: 4, center: address });
const marker = new google.maps.Marker({ position: address, map });
})();

What actually worked for me was:
In the HTML file, I removed the async attribute from the <script> tag.
In the JavaScript file, I double-checked that the function was outside the $(document).ready function.

Could be your initMap function is in a $(document).ready function. If it is then it won't work, it has to be outside of any other functions.

In addition to #DB.Null's answer, I used Function.prototype as no-op (no-operation) function on #3 instead of angular.noop (I don't have angular in my project).
So this...
<script async defer src="https://maps.googleapis.com/maps/api/js?key=API_KEY_HERE&callback=Function.prototype" type="text/javascript"></script>

In my case there was a formatting issue earlier on, so the error was a consequence of something else. My server was rendering the lat/lon values with commas instead of periods, because of different regional settings.

I removed the callback of "initMap" from the script tag and placed the callback in my JavaScript file.
<script async src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEYWY&callback=initMap"></script>
was changed to
<script async src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEYWY"></script>
So the first JS tag is mine.
<script async type=module src="js/app.js"></script>
<script async src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEYWY"></script>
I called the initMap function from within my JS file by adding the following to my app.js file.
initMap();

This is the top result when you Google 'initMap function is not a function' so although there are a lot of answers already, I will make it very clear for the rookies like me what is happening.
To load a Google Map your page need 3 things:
A <div id="map"></div>
Load the Google Map javascript.
A function to initiate the map.
The div (easy)
The div is easy. The Google Map Javascript is easy but there is a catch.
Load Google Maps Javascript
<script
src="https://maps.googleapis.com/maps/api/jskey=YOURKEY&callback=initMap"
defer
></script>
There are two important parts of this script. defer, and the callback=initMap
First let's talk about defer. The defer attribute causes the callback to execute after the full HTML document has been parsed. This makes sure your div is loaded and ready to be used by the javascript. However, does not mean that the Google Maps Javascript will load after your Javascript, and that is why the error happens sometimes.
The init function
You need to call a function to run the code that sets up the Google Map. The callback makes it easy because once the Google Maps code loads, it will call a function you tell it to load the map.
I'll repeat that because it is important. The callback=initMap is calling the initMap function in your code.
So if your code hasn't loaded by the time the Google Maps javascript loads, it will call initMap but you get the error because it doesn't know what initMap is because your code, where you define initMap hasn't loaded yet.
This can happen seemingly randomly as network times vary. Sometimes you code loads first, and it works. Sometimes your code doesn't load first and Google Maps code loads first and you get the error.
Solution
There are a lot of solutions, but the one that I like is don't set a callback, and init the Map yourself after you know everything is loaded.
This way, we can load Google Maps Javascript async, which is faster since it loads in a separate process and doesn't wait for anything.
<script async
src="https://maps.googleapis.com/maps/api/js?key=YOURKEY">
</script>
Notice how there is no callback set. So Google Map Javascript will not automatically call my init function, I have to call it when I know everything is ready.
We can do that with a window load event listener.
window.addEventListener("load", docReady);
function docReady() {
initMap();
}
The window load event fires after everything including Javascript has loaded.
Of course this might slow the page down as it has to load everything, including images, and then it will fire and initialize the map, but it ensures it will work every time.
As you learn more, you will learn other ways to make sure things load in the correct order. Good luck!

This solution helped me:
this.initMap = () => {};
...
src="https://maps.google.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"

In my case, I had to load the Map on my Wordpress website and the problem was that the Google's api script was loading before the initMap(). Therefore, I solved the problem with a delay:
<script>
function initMap() {
// Your Javascript Codes for the map
...
}
<?php
// Delay for 5 seconds
sleep(5);
?>
</script>
<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEYWY&callback=initMap"></script>

Related

Capture with Angular the response object sent by the render() method in Symfony 2

Is it possible to get the response object sent by the render() (Symfony2) method?
An example:
public function indexAction(){
$params = array('Hi' =>'Hello world', 'userName' =>'Jack');
return $this->render('exampleBundle:Default:index.html.twig', $params);
}
I'm totally able to capture the var $params and its content in the html.twig file, but I can't figure out how to get that content it in javascript to render the view using angular. Just in case, I tried many absurd things, but they didn't work at all obviously.
Maybe it isn't possible at all and I'll need to redesign it or make a new ajax call once the document is loaded instead of passing the content via the render() method. I'm not really sure so, can I reach this without make an ajax request?
This approach seems to me a bad thing. Most of modern client applications philosophy is to to load as soon as possible the web page from server and the load data through xhr request.
Said that if you need or want to do it, you can achieve it in differents manners:
I did not tried the examples, so this can contains some syntax errors:
1- JS Vanilla approach; in your html.twig
<script type="text/javascript">
window.nameSpaceOfMyApp = window.nameSpaceOfMyApp || {};
window.nameSpaceOfMyApp.exchange = window.nameSpaceOfMyApp.exchange || {};
window.nameSpaceOfMyApp.exchange.params = JSON.parse({{$params |json_encode()}});
</script>
2- More "Angular Way" Not overloading global namespace and better testing...If you have load angular.js at this point you can create in twig template the main module of your angular App (if you cant use your main module, you can use a new module and require it as dependency of your main module)
in your html.twig:
<script type="text/javascript">
angular.module('moduleName',[<dependencies>,...])
.value('exchange',{
params: JSON.parse({{$params |json_encode()}})
})
</script>
Later you can inject this value where you need it.
angular.module('moduleName').controller('nameOfController',['exchange', function(exchange){
console.log(exchange.params)
}]);

ngCordova getCurrentLocation causes blank google map

I know there are a ton of support forums, but it seems that no one quite has the answer that I'm searching for. So, in detail, the below is my scenario (I am developing in ionic frame).
I am using ngCordova cordova-plugin-geolocation to obtain my current location (this works fine):
function initialize(){
var posOptions = {timeout: 10000, enableHighAccuracy: false};
$cordovaGeolocation.getCurrentPosition(posOptions)
.then(function(pos){
var lat=pos.coords.latitude;
var lng=pos.coords.longitude;
var myLatlng = new google.maps.LatLng(lat,lng);
initMap(myLatlng);
}, function(err){
});}
I have verified that with the above code that vars lat,lng are populated with correct values. So, next thing I want to do is render my google maps to be displayed, I do so via initMap function below:
function initMap(origin){
var mapOptions = {
center: origin,
zoom: 16,
};
$scope.map=new google.maps.Map(document.getElementById("map"),mapOptions);}
When I attempt to render this image, I receive only a blank screen. Also, I have 'ionic.Platform.ready(initialize);' in my controller code.
NOTE: The below is a code segment in my html template:
<div id="map" ng-controller="MapCtrl">
</div>
Some failed coding attempts include:
using ng-init() as opposed to ionic.Platform.ready - same blank image
taking everything out of function (desperate attempt) - same blank image
Successful when:
I call initMap directly with latlong google object, completely bypassing the "getCurrentPosition" operation, which is nested in the initialize() function.
Any recommendations will be really appreciated.
So, the issue with my initial logic was that executing getCurrentPosition is an asynchronous call, meaning it will receive its data after the DOM is fully loaded. so, In order to fix this, I use $q.defer() to create a promise object to be accessed later. more information on how to do this can be found on another forum I posted to:
https://forum.ionicframework.com/t/ngcordova-getcurrentposition-with-google-maps-api/44153
Good luck and happy coding!

Google maps not always fully rendering in Ionic

Having trouble with always rendering google maps in my Ionic app. When I first land on a view from a list of items on the previous view, the map always renders in its complete state. However, if I go back to the previous view and tap a different business, or even the same one, it appears as if the map is only rendering 25% of the complete map. I'm having this issue on both the emulator and on my iPhone.
Example
Code
getData.getBusinesses()
.then(function(data) {
// get businesses data from getData factory
})
.then(function(data) {
// get businesses photo from getData factory
})
.then(function(data) {
// get some other business stuff
})
.then(function() {
// get reviews for current business from separate async call in reviews factory
})
.then(function() {
// instantiate our map
var map = new GoogleMap($scope.business.name, $scope.business.addr1, $scope.business.city, $scope.business.state, $scope.business.zip, $scope.business.lat, $scope.business.long);
map.initialize();
})
.then(function() {
// okay, hide loading icon and show view now
},
function(err) {
// log an error if something goes wrong
});
What doesn't make sense to me is that I'm using this exact code for a website equivalent of the app, yet the maps fully load in the browser every time. The maps also fully load when I do an ionic serve and test the app in Chrome. I did also try returning the map and initializing it in a following promise, but to no avail.
I've also tried using angular google maps, but the same issue is occurring. I think I might want to refactor my gmaps.js (where I'm creating the Google Maps function) into a directive, but I don't know if that will actually fix anything (seeing as angular google maps had the same rendering issue).
I don't think the full code is necessary, but if you need to see more let me know.
EDIT
It seems that wrapping my map call in a setTimeout for 100ms always renders the map now. So I guess the new question is, what's the angular way of doing this?
I'm seeing similar issues with ng-map in Ionic. I have a map inside of a tab view and upon switching tabs away from the map view and back again, I would often see the poorly rendered and greyed out map as you describe above. Two things that I did that may help fix your issue:
Try using $state.go('yourStateHere', {}, {reload: true}); to get back to your view. The reload: true seemed to help re-render the map properly when the map was within the tab's template.
After wrapping the map in my own directive, I found the same thing happening again and wasn't able to fix it with the first suggestion. To fix it this time, I started with #Fernando's suggestion and added his suggested $ionicView.enter event to my directive's controller. When that didn't work, I instead added a simple ng-if="vm.displayMap" directive to the <ng-map> directive and added the following code to add it to the DOM on controller activation and remove it from the DOM right before leaving the view.
function controller($scope) {
vm.displayMap = true;
$scope.$on('$ionicView.beforeLeave', function(){
vm.displayMap = false;
});
}
Hope that helps.
don't use setTimeout on this!
You need to understand that the map is conflicting with the container size or something (example: map is loading while ionic animation is running, like swiping).
Once you understand this, you need to set map after view is completely rendered.
Try this on your controller:
$scope.$on('$ionicView.enter', function(){
var map = new GoogleMap($scope.business.name,
$scope.business.addr1, $scope.business.city,
$scope.business.state, $scope.business.zip,
$scope.business.lat, $scope.business.long);
map.initialize();
});

Initialize my angularJs App after Phonegap deviceready

I have a phonegap app using AngularJS.
On my app I am using the NetworkStatus plugin to confirm the type of connection the mobile phone is using.
On my root route I am resolving a call to a Service which call DeviceService and it responsbile to access the navigator.network.connection.type and decide if the connection is on or off. the resove send to the controller (through route resolve functionality) a connectionState variable which declare the state of the connection.
On that route I would like to throw an error if Connection is not available.
Having said that, my problem is the the DeviceReady event is fired after my route is accessed. so my route resolve unable to complete the connection verification.
How can I sync up that my angular app will only start after DeviceReady event is fired ?
Getting the injector module error from AngularJs usually means that you either mispelled the name of the module or angular simply did not find it.
If the Angular app works properly on its own (e.g when not wrapped in phonegap), this means that this issue is in the order the things happens when your index.html is loaded.
Cordova/PhoneGap loads your index page
Its Webview parses it and loads its scripts tags
If some code is not wrapped in functions or objects, it's executed straight away
Phonegap sends the event deviceready to tell your app that its bridges with native code are ready
The last 2 operations can occur in both orders but most often in the one I gave you.
Thus, if you put your angular module name on the html or body tag through ng-app for instance, angular will try loading it when it finds it.
So, for it to work, you need to :
Remove YourAppName from html/body tag
Create your angular module normally (its name must match in bootstrap and module calls)
Use the deviceready event as the trigger to boostrap your application
For example (short example, nothing but css in head) :
<body>
<div class="app">
<h1>PhoneGap</h1>
<div id="deviceready" class="blink">
{{2+2}}
</div>
</div>
<script type="text/javascript" src="phonegap.js"></script>
<script type="text/javascript" src="js/angular.min.js"></script>
<script type="text/javascript">
document.addEventListener('deviceready', function onDeviceReady() {
angular.bootstrap(document, ['YourAppName']);
}, false);
var YourAppName = angular.module('YourAppName', []);
</script>
</body>
If you want to understand this on your own, I recommend putting some console.log to get the order of things.
You can aslo use Chrome DevTools remote debugging which works pretty well If you have Chrome 32+ on your pc and android 4.4 on phone, or only pc and you debug on emulator. Quite nice to see the errors and stuff.
Debugging webviews is a bit strange at first but really useful to trace errors !
Hope this helps
You need to do a manual angular.bootstrap (instead of using ng-app):
deviceReady = function() {
angular.bootstrap(document, ['app']);
};
window.addEventListener('deviceready', deviceReady, false);
I'm using the following solution, which allows AngularJS to be bootstrapped when running with Cordova as well as when running directly in a browser, which is where much of my development takes place. This makes sure that Angular starts up after Cordova/Phonegape is ready, and still ensures that it works when there is no Cordova/PhoneGap at all.
// This is a function that bootstraps AngularJS, which is called from later code
function bootstrapAngular() {
console.log("Bootstrapping AngularJS");
// This assumes your app is named "app" and is on the body tag: <body ng-app="app">
// Change the selector from "body" to whatever you need
var domElement = document.querySelector('body');
// Change the application name from "app" if needed
angular.bootstrap(domElement, ['app']);
}
// This is my preferred Cordova detection method, as it doesn't require updating.
if (document.URL.indexOf( 'http://' ) === -1
&& document.URL.indexOf( 'https://' ) === -1) {
console.log("URL: Running in Cordova/PhoneGap");
document.addEventListener("deviceready", bootstrapAngular, false);
} else {
console.log("URL: Running in browser");
bootstrapAngular();
}
If you run into problems with the http/https detection method, due to, perhaps, loading a Cordova app into the phone from the web, you could use the following method instead:
// This method of user agent detection also works, though it means you might have to maintain this UA list
if (navigator.userAgent.match(/(iOS|iPhone|iPod|iPad|Android|BlackBerry)/)) {
console.log("UA: Running in Cordova/PhoneGap");
document.addEventListener("deviceready", bootstrapAngular, false);
} else {
console.log("UA: Running in browser");
bootstrapAngular();
}
You would still need the bootstrapAngular function from the first example, of course.
This method no longer works in iOS 9:
if (document.URL.indexOf( 'http://' ) === -1
&& document.URL.indexOf( 'https://' ) === -1) {
console.log("URL: Running in Cordova/PhoneGap");
document.addEventListener("deviceready", bootstrapAngular, false);
} else {
console.log("URL: Running in browser");
bootstrapAngular();
}
You shouldn't use such workarounds.
"cordova.js" isn't resolved in the browser, thus the "deviceready" event never gets fired. Just call it manually in the browser console for testing.
var customDeviceReadyEvent = new Event('deviceready');
document.dispatchEvent(customDeviceReadyEvent);

dijit.byId("").is not defined in Worklight works with Angularjs

I make a project in worklight used dojo mobile 1.8.1 and angularjs 1.0.1,but i got a strange problem.
Here is my html part:
<div data-dojo-type="dojox.mobile.ScrollableView" data-dojo-props="selected:true" id="id1" ></div>
<div class="full" data-dojo-type="dojox.mobile.View" id="id2"></div>
and my JavaScript part:
require([
"dojo", "dijit/_base/manager","dojo/parser", "dijit/registry",
], function(dojo) {
dojo.ready(function() {
// dijit.byId("id1").performTransition("id2"); //////////place I
});
});
var angularApp = angular.module('app', [])
.run(['$rootScope','$templateCache','$route',
function($rootScope,$templateCache,$route) {
dijit.byId("id1").performTransition("id2");////////place II
}]);
The problem is at place I, it works well, but when I put "dijit.byId("id1")" at place II, it shows:
dijit.byId("").is not defined
The ready function is executed after dojo parsed your document & constructed the widgets you try to get using dijit.byId.
The second part is not placed within the ready function, so dojo can't find your elements yet !
Solution: Access your elements in the ready function OR do not declare them declaratively (like you did, using html code...) !
Lucian
The dojo.ready() function registers an event-handler function (callback) which will be fired after the DOM got completely parsed.
This comes in very handy if you want to be sure that every html element got rerendered as dojo-widget before you perform operations on them.
So, in your example, Code II will be executed before the dijit.byId() function has been made available by loading the necessary modules (dijit/registry, ...). Code II would only work after the dom-ready event got fired and your "dojo.ready()" function did load the required modules.
You should definately invest 5 minutes in reading what dojo.ready() is about:
http://dojotoolkit.org/reference-guide/1.8/dojo/ready.html
Sidenote:
You shouldn't use dijit.byId() any more in Dojo 1.8.1. Try using dijit.registry.byId() (you have to require the dijit/registry module).
http://dojotoolkit.org/reference-guide/1.8/dijit/registry.html#dijit-registry-byid

Resources