I'm using the cloud enpoints demo with AngularJS and I'm running into an infinite loop with their suggested method of running the authorization after the client.js is loaded. Here is the suggested method.
First, after all other script tags (for Angular and other JS files, I'm doing this):
<script>
function init() {
window.init();
}
</script>
<script src="https://apis.google.com/js/client.js?onload=init"></script>
Then, in a controller, I handle the window init like:
$window.init = function () {
// Loads the OAuth and helloworld APIs asynchronously, and triggers login
// when they have completed.
var apisToLoad;
var callback = function () {
if (--apisToLoad == 0) {
googleAPI.signin(true,
googleAPI.userAuthed);
appContext.appReady = true;
alert('loaded');
}
}
apisToLoad = 2; // must match number of calls to gapi.client.load()
gapi.client.load('helloworld', 'v1', callback, googleAPI.apiRoot);
gapi.client.load('oauth2', 'v2', callback);
};
What I think I'm finding is that there is a race condition here where the $window.init is not set up early enough so I end up with the message:
Uncaught RangeError: Maximum call stack size exceeded
This is due to the fact that the "window.init()" just calls back to the init() function and exceeds the stack.
Any suggestions on how I can better handle this? Thanks.
The first line is creating an infinite loop there because you are calling window.init inside the actual window.init.
<script>
/**
* Initializes the Google API JavaScript client. Bootstrap the angular module after loading the Google libraries
* so that Google JavaScript library ready in the angular modules.
*/
function init() {
gapi.client.load('conference', 'v1', null, '//' + window.location.host + '/_ah/api');
gapi.client.load('oauth2', 'v2', callback);
};
</script>
<script src="//apis.google.com/js/client:plusone.js?onload=init"></script>
You can try this code to see if makes more sense for you
Looks like your angular controllers are not loading/executing in time, can't tell why but you could wait for document ready, in true jQuery fashion:
function init() {
angular.element(document).ready(function() {
window.init();
});
}
Angular should've finished loading by then.
By doing this, you're telling window.init to call itself, creating an infinite loop.
<script>
function init() {
window.init();
}
init===window.init; // => true
</script>
<script src="https://apis.google.com/js/client.js?onload=init"></script>
If you look at my code more closely, you'll see that I name the functions differently, like so:
<script>
function init() {
window.initGapi();
}
init===window.initGapi; // => false
</script>
<script src="https://apis.google.com/js/client.js?onload=init"></script>
Then simply define initGapi in your controller instead:
$window.initGapi = function () {}
The code in the comments to the accepted answer waits until the api is loaded to bootstrap the app, which takes longer.
Put this in the "I missed something really basic" bin:
I noticed that I forgot something in my controller definition:
topNavBar.$inject = ['$location', 'appContext', 'logger'];
function topNavBar($location, $window, appContext, logger) {
Notice, no '$window' in the inject, so it got the definition for appContext and doing a "$window.init = " had absolutely no effect.
Related
I have build an angularjs application. there are many places I used javascript console functions.
now I want to use a single variable to turn off those console.
I dont want to check the variable everytime whenever I use the console function.
so I decided to make a service to handle that process.
Console factory
AppModule
.factory("$console", function (ENV) {
function log (txt) {
var args = arguments;
if(ENV.debug) {
console.log.apply(this, args);
}
}
...
...
});
And I called that function like the following way.
Controller
AppModule
.controller('CommonCtrl', function ($scope, $console
$scope.personalInfo = function () {
$scope.errmsg = false;
getPersonalInfo(function (data) {
if(!$scope.errmsg) {
$console.log("userdatainfo:",data);
}
...
...
})
});
All is working perfectly.
But Only the problem is I can only see the line number of the factory file on inspect panel.
I need to have the line number from where the factory function is getting called (like the line no of the above controller file).
Please reply with valuable suggestion.
This is a javascript feature and not specifically Angular. You can use arguments in a function like this. However the line number is not given but you get the caller name.
function Hello()
{
console.log("caller is " + arguments.callee.caller.toString());
}
I have an AfterFeatures hook that I'm using to try to gracefully shut down an expressjs web server that is used for testing only. In this hook, I need to call the visit method, which has been added to World, but I apparently don't have access to World from within this hook. What can I do to gain access to things in World inside this and other hooks?
// features/support/after_hooks.js
var myAfterHooks = function () {
this.registerHandler('AfterFeatures', function (event, callback) {
this.visit('/quit', callback);
});
};
module.exports = myAfterHooks;
I do not think you can. In the AfterFeatures the cucumber process has already finished, so this no longer references it.
But, if all you want is to visit a page, you can register your browser outside cucumber so that it is still accessible from the AfterFeatures hook. If you are using AngularJS + Protractor, Protractor handles the browser for you, and therefore it is still accessible in the AfterFeatures hook. It would be the same principle. This can be done with the following.
hooks.js
var myHooks = function () {
this.registerHandler('AfterFeatures', function (event, callback) {
console.log('----- AfterFeatures hook');
// This will not work as the World is no longer valid after the features
// outside cucumber
//this.visit('/quit', callback);
// But the browser is now handled by Protractor so you can do this
browser.get('/quit').then(callback);
});
};
module.exports = myHooks;
world.js
module.exports = function() {
this.World = function World(callback) {
this.visit = function(url) {
console.log('visit ' + url);
return browser.get(url);
};
callback();
};
}
The AfterFeatures example in the cucumber-js GitHub repository is a little misleading, as it looks like you can access the driver that you previously registered in the World. But if you are using pure cucumber-js only, I have not seen that work.
By the way, you can use just this instead of registerHandler.
this.AfterFeatures(function (event, callback) {
browser.get('/quit').then(callback);
});
Hope this helps.
I'm following this angular tutorial and am confused by the $on("loaded", ...) calls.
$scope.user.$on("loaded", function() {
populatePosts();
populatecomments();
});
...
function populatecomments() {
$scope.comments = {};
angular.forEach($scope.user.comments, function(comment) {
var post = Post.find(comment.postId);
post.$on("loaded", function() {
$scope.comments[comment.id] = post.$child("comments").$child(comment.id);
$scope.commentedPosts[comment.postId] = post;
});
});
}
Why can't we just call populatePosts() and populatComments() here? Why is a second $on("loaded", ...) needed within populatecomments() if it isn't called until loaded?
The first:
$scope.user.$on("loaded",...
will be triggered when $scope.user is loaded, the second:
post.$on("loaded",...
will be triggered for each post when the given one is loaded.
Angular(and mostly javascript) does network calls asynchronously, so if you want to postprocess their result, you have to define a callback which will be called when they are loaded.
As you see populatecomments() uses $scope.user.comments which could be inaccessible if you call populatecomments() directly, outside of the loaded event handler.
In this case, it's not. You could (and should) refactor this code to use promises instead.
I'm abstracting away the code to focus on the core question. I have a main.js file, which requires a second file, optionsmod.js.
I'd like to be able to send a message from optionsmod.js to main.js, so that main.js can then go on to do other things. I don't know how to do this...
Here is an example that does't work.
main.js:
var optionsmod = require("optionsmod.js");
var self = require("sdk/self");
optionsmod.init();
self.port.on("message",function(){
console.log("message received");
});
optionsmod.js:
var self = require("sdk/self");
function init(){
console.log("here in init");
//FOR THIS EXAMPLE, I'VE MADE THE CALL HERE. BUT WONT BE NORMALLY
sendMessage();
}
function sendMessage(){
self.port.emit("message");
console.log("message sent");
}
exports.init = init;
The code I've added doesn't work, but is there a way to do something similar?
There is no default way of passing messages between modules. It is quite easy to get something to happen in optionsmod.js when an event occurs in main.js. Simply export the function and call it from main.js. It isn't so straightforward the other way around, though. Two ways I handle this are by passing callback functions and creating event targets. Here's an example with a callback function:
main.js
var optionsmod = require("optionsmod.js");
var eventCallback = function(message) {
console.log('message received: '+message);
};
optionsmod.init(eventCallback);
optionsmod.js
exports.init = function(eventCallback) {
foo.on('bar', function() {
eventCallback('the message');
console.log('message sent');
});
};
The alternative is to export foo, then call foo.on from main.js, but that probably defeats the whole purpose of writing a separate module, in which case the docs I linked to will be helpful. Frankly, you could probably use those docs to create your own proprietary messaging system, but I think you're better off thinking in the above terms.
I was implementing a on-demand script controller based on jquery's getscript, it looks like this:
function controller = function(){
var script = function(){
var scripts = {};
return {
load: function(jsurl){
$.getScript(jsurl, null);
},
run: function(js){
window[js].apply(this,null);
}
}
};
return {
script: script()
};
}
var ctlr = controller();
then here is a remote script with a function to be loaded - remote.js
function remotefunc(){
alert( 'remotefunc invoked' );
}
and here is how the whole thing supposed to work, in the main script:
ctlr.script.load( 'remote.js' ); // remote script successfully loaded
ctlr.script.run( 'remotefunc' ); // got an error, window['remotefunc'] undefined
but as you can see, 'remotefunc' is defined in the global 'window' scope, so the window object is supposed to be able to 'see' it.
I thought the problem was probably the closure stuff in the 'controller' definition, so I did a direct $.getScirpt without using the 'controller':
$.getScript( 'http://path/to/remote.js', function(){
window['remotefunc'].apply( this, null ); // this worked
} );
strange. So it is about the 'controller' implementation(I kind need it)! Anybody can help me out with this? How to fix the 'controller' implementation so the
window[js].apply(this,null);
can actually work?
Thanx.
The reason it's telling you window['remotefunc'] is undefined is because you are not giving it time to actually download and execute the remote script before attempting to call a function defined in it.
The remote script is loaded asynchronously, which means the script execution isn't paused while waiting for a response.
You will need to either re-implement the getScript method to be synchronous or somehow work your class around the fact that the function will not be available in any determinate amount of time.
EDIT: Just found another possible solution, try calling this before your request
$.ajaxSetup({async: false});
This will make the getScript method synchronous
When using something like getSript, it's important to remember that it is fetching asynchronously. Meaning, the browser fires off the request and while that's happening, code after that line executes without pause.
jQuery provides a callback function parameter to get script that allows you to do something after the asynchronous fetch is finished.
Try this:
var script = function(){
var scripts = {};
return {
load: function(jsurl, callback){
$.getScript(jsurl, callback);
},
run: function(js){
window[js].apply(this,null);
}
}
};
Then, when using it:
ctlr.load( 'remote.js', function(){
// remote script successfully loaded
ctlr.run( 'remotefunc' );
});
Could this be a timing issue?
In your working example you call the function in a callback which jQuery will not invoke until the script is loaded. In your non-working example, you call the function immediately after getScript which is asynchronously loading the script.