I have seen numerous flavors of this question, but cannot seem to find the right answer for my issue.
The problem with the following is the service cannot share data between the other modules/controllers. It does not act as a singleton. Meaning, if I add windows in dashboard and call windowService.getWindows() I will see the windows I added. But if I do the same from myWidget after adding them via the dashboard, or vice versa, the collection in the service is empty as if the other module/controller hasn't added anything. What am I doing wrong here? I expected a single collection of windows to be retained by the service and be accessible from either of my other modules.
Module 1
var dashboard = angular.module('dashboard', [windowServiceModule]);
dashboard.controller('DashboardController', function DashboardController(windowService) {...stuff});
Module 2
var myWidget = angular.module('myWidget', [windowServiceModule]);
myWidget.controller('MyWidgetController', function MyWidgetController(windowService) {...stuff});
Service
var windowServiceModule = angular.module('windowService', []);
windowServiceModule.service('windowService', function() {
var windows = [];
this.addWindow = function(win)
{
this.windows.push(win)
}
this.getWindows = function()
{
return this.windows;
}
});
EDIT: while walking through other answers, I tried this: Share a single service between multiple angular.js apps
That exposed a part of the problem, I think. The above suggests iterating over a $rootScope collection, but in my case, there is only one $rootScope in the collection and it is not the same between dashboard and myWidget. As it is, myWidget is in an iframe and now I'm thinking I need to take a different approach, currently testing a solution similar to the link I left in the comments.
Ok, I'm pretty sure my initial code would have worked fine if the myWidget wasn't being loaded in an iframe. I have a dashboard that can open windows embedded or popped out and I wanted to use the service to manage the windows and handle passing state back and forth since popping out the iframe requires a reload of the page.
Anyways, I now have something like this in place in the service and it is working:
var windowServiceModule = angular.module('windowService', []);
windowServiceModule.service('windowService', function($window) {
...stuff
var parentScope = $window.parent.getScope();
...other stuff
After that you can access anything in the parent's scope with parentScope. I pass in the angular $window.
Just for reference, getScope() already existed in the parent JSP file. This is what is looks like:
function getScope()
{
return angular.element($("#DashboardController")).scope();
}
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>
I'm building a site. First I made myself familiar with AngularJS in general and tried to make sense of all the new things for me: grunt, livereload, angularjs... I soon realized some mistakes I made and started building a new project from the ground up.
When I was building the site whilst on the grunt server. It displayed me more messages than it is now with gulp. I'm not sure why is that?
For example, since I was moving some existing code from my "study-project" to a new clean-start-gulp-project. I forgot to add one service: FlashService - responsible for displaying simple messages on my site.
And I was already using that as a dependency in another service. But for some reason all I was presented was a blank site with no messages in the console. Whereas in my previous project I always got messages like "No such provider found" or something similar. Can anybody help me make debuging easier?
Some other things have changed also. I changed the general folder structure of the code and I'm trying to modularize my new app. Currently I can't think of anything else that might even be remotely related.
My current folder structure is like this:
src/app
app.js
/services
auth-service.js
flash-service.js
Some important parts from my code:
app.js
angular.module('myapp', [
...
'myapp.home'
]).config(function ($stateProvider) {
$stateProvider.state('main', {
'abstract': true,
resolve: {
promiseUser: ['AuthService', function (AuthService) { AuthService.promiseUser; }]
},
template: '<div ui-view></div>'
});
})
AuthService
angular.module('myapp').service('AuthService', ['FlashService', function (FlashService){
// As soon as I created the missing service 'FlashService' everything started working.
// Or when I simply removed the FlashService from dependencies.
// What I don't understand is why didn't I get any errors in the console?
}
Since I already spent too much time looking the bug in a wrong place I can only imagine that some other messages might also not make it into the console. This would eventually make debuging quite hard.
What am I missing here? Why didn't I get any errors in the console?
Edit:
The first project was generated using Yeoman and for the second one I used gulp slush. Don't think this will help, but I figured might be worth mentioning.
Edit2
Found out that if I change angular.js and add console.log(message) inside the first minError function, I almost get what I want.
function minErr(module) {
...
console.log(message);
return new Error(message);
}
Now I just need to figure out an alternative way to do this or what was different with yeoman generated application and why I am not getting these messages by default:
[$injector:unpr] Unknown provider: aProvider <- a <- AuthService
http://errors.angularjs.org/1.2.25/$injector/unpr?p0=aProvider%20%3C-%20a%20%3C-%20AuthService
Loading modules with Angular if you want error messages always try and catch. My guess that gulp slush is not generating a try/catch scenario for loading modules while yeoman does. The difference between gulp and grunt shouldn't matter so long as you are trying to catch the error. The thing with gulp is that it is all process based some of something failed in the process sometimes the process will just sit there or just stop all together.
Disclaimer: I have never generated a project with slush only yeoman.
Okay, so I'll try to explain what I figured out.
This had nothing to do with gulp nor grunt. Also the directory structure was completely irrelevant.
First, let's take a look at some of the most important parts of both config files.
current:
angular.module('myapp', [])
.config(function ($stateProvider) {
$stateProvider.state('main', {
'abstract': true,
resolve: {
promiseUser: ['AuthService', function (AuthService) { AuthService.promiseUser; }]
},
template: '<div ui-view></div>'
});
})
...
before:
angular.module('myapp', [])
.config(function ($stateProvider) {
$stateProvider.state('main', {
'abstract': true,
resolve: {
promiseUser: ['AuthService', function (AuthService) { AuthService.promiseUser; }]
},
template: '<div ui-view></div>'
});
})
...
.run(['AuthService', function (AuthService) { ... }]);
...
So the first part is the same in both cases. What was different before is that I also used AuthService inside a run block.
I was checking that on state change the authenticated user had permissions to access the new state.
So what happened inside the resolve stayed inside the resolve! But what happened inside the run block was displayed in the console.
By modifying these parts of the code - both projects displayed the same errors and had the same behavior.
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
I am using backbone.js to create a single page app. I am new to backbone, so please forgive any wrong semantics.
My Problem is when rendering the views.
Initially, I have a javascript in my index.html that executes the some dom manipulation(image slider).
The JS is wrapped in $(window).load() so all is fine on initiation.
The code obviously doesn't execute unless the page is loaded from url. the code will not run from backbone views or router. So the page loads without the dom manipulation.
I have tried to insert my code into the render and initialize function in the view, but to no avail. Should I add this code to the router? that seems to be a bit of a hack.
Where should I include the "dom ready" code?
and / or is there a better way to manage views and their dom elements on load in backbone?
the code:
home.js
window.HomeView = Backbone.View.extend({
initialize:function () {
this.render();
},
render:function () {
$(this.el).html(this.template());
this.startOrbits();
return this;
},
startOrbits:function(){
$(window).load(function() {
$('#orbit-main').orbit({ fluid: '16x6', swipe:true });
$('#orbit-news').orbit({ fluid: '8x6', bullets: true, directionalNav:false, captions:true, advanceSpeed: 9000});
});
},
});
But when I go to another view, then back, the code obviously doesn't
excite
I'm not quite sure what that means. Leaving the "excite" part aside, you don't "go to" views; views are just ways of adding elements to the page, or adding logic to existing elements.
If I had to guess though, I'd imagine that you're using the Backbone router to move between virtual "pages" (and you use views to make those pages). If that's the case, you need to look at the Backbone router events:
http://documentcloud.github.com/backbone/#Router
http://documentcloud.github.com/backbone/#FAQ-events
Specifically, I think you want to bind an event handler (on your router) to "route:nameOfYourRoute", or just :route" (if you want to trigger your logic on every virtual page load).
Hope that helps, and if my guesses are wrong please edit your question to clarify.
I was able to find a solution.
After commenting out the if statement in my router function, things went smoothly.
home: function () {
// if (!this.homeView) {
this.homeView = new HomeView();
// }
$('#main-content').html(this.homeView.el);
this.homeView.startOrbits();
this.headerView.selectMenuItem('home');
},
I do realize that this means I create a new view on every rout trigger.
Please feel free to offer more optimal solutions.