How to pass an argument to a Javascript Module? - javascript

I want to use a Javascript Module (JSM) in a single window of my Xul application, so I can load the resource as I need it.
But, I need to pass the window to the JSM, and I don't know how to do it. Follows my attempt:
In my resource.jsm:
var EXPORTED_SYMBOLS = ["hello"];
function hello(win) {
win.alert("ALERT FROM JSM!");
}
calling in my window with:
Components.utils.import("resource://module/resource.jsm");
hello(window);
but I get:
win is undefined
in the resource.jsm.
Any idea how to make it work?

It might be causing problems that you named the parameter for your hello function to be window. While window isn't a reserved word, most browser environments treat it as an unassignable constant of sorts. Try:
function hello( obj ) {
obj.alert("ALERT FROM JSM!");
}
in your module and then invoke it with hello(window), hello(document.window), or hello(this)
After reading the Javascript Module documentation, it looks like you'll need to create an object within the module and then change it's property by reference. So in your JSM:
var EXPORTED_SYMBOLS = ["params", "hello"];
params = {
win: this
};
function hello() {
params.win.alert("ALERT FROM JSM!");
}
Then you'd invoke by first assigning the window to that parameter and then calling the function:
Components.utils.import("resource://module/resource.jsm");
params.win = window;
hello();
Note: I am not familiar enough with JSMs to know if there's a better way to do this, but this should work.

Related

How can I use modules in the browser, but also refer to variables and functions from within DevTools?

I have my HTML setup like this:
<script type="module" src="main.js"></script>
and all the ES6 modules work fine. The only problem is I now can't refer to anything from within DevTools (like using the Console and typing in a variable to see it's value or using a function manually).
How do I import modules whilst being able to use the DevTools? Thanks!
One way to make a variable accessable within DevTools is to create it on the window object:
// Variable in your module
var importantNumber = 1;
window.importantNumber = importantNumber;
This method works fine if you just have a couple of variables, but if you need to have access to a lot more variables within DevTools, I would recommend you go to the sources-tab in DevTools, search for your module and adding a breakpoint. When the execution pauses, you have access to all the variables within that module on the DevTools console.
If you want to be able to refer to variables created within the module from the console's global scope, you'll have to deliberately expose each such variable that you want to be visible from the console. Either assign each variable to window (probably not a good idea - the whole point of modules is to make things more modular, without global pollution), or perhaps assign a single object to window, to which you assign module variables. For example:
// in the console:
setTimeout(() => {
window.myModule.foo();
console.log(window.myModule.bar);
});
<script type="module">
function foo() {
console.log('doing foo');
}
const bar = 'a string bar';
const thisModule = { foo, bar };
window.myModule = thisModule;
// If you ever reassign variables, you'll have to reassign them on thisModule too
// or, only reference and reassign properties of thisModule, rather than create independent variables
</script>
For anyone else interested, if you're comfortable with it, use a bundler like Webpack. I don't believe (at least at this point) that the browser will by itself be able to use the DevTools on modules (the other solutions are quite janky, and aren't fantastic to work with).
Hopefully in the future, when all major browsers will be able to support ES6 modules without a bundler, we'll be able to use DevTools.
Using a Helper
I personally use a little helper function in development that allows me to expose a bunch a variables in a single expression. For example, it makes the following two blocks equivalent:
window.playerOne = playerOne;
window.someClass = someClass;
window.localData = localData;
globalize({playerOne, someClass, localData});
The helper looks like this:
const globalize = function(variables) {
Object.entries(variables).forEach(([name, value]) => window[name] = value);
};

How to overcome issue of ".. is not a function" while referring to earlier defined function expression in same js file

On accessing function expression in the same file, gives an error of .. "is not a function".
I need this below function expression to be available both outside the js file to other .js files and as well inside the same js file.
I have tried below things from below blogs, nothing seems to work
https://github.com/nodejs/node/issues/2923
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Not_a_function
// this is in abc.js
function qpValidations() {
this.addDaystoGetDate = function(noOfDays){
...
}
this.constructDate = function(){
this.addDaystoGetDate(2);// here issue is coming, where trying to
//call function in same .js file
}
}
module.exports = new qpValidations();
Any help is most appreciated!!, though this issue occurred many times with me, tried avoiding file circular dependency and as well clubbing function expression and declaration, had solved the issues earlier, but now again it has poped up not sure what the root cause of this issue..?
If you're only having issues calling the function in the same file, it's hard to help because you haven't shown HOW you're trying to call it. But I would suggest you try something like this:
// this is in abc.js
function qpValidations() {
this.addDaystoGetDate = function(noOfDays){
...
}
this.constructDate = function(){
this.addDaystoGetDate (2);// here issue is coming
}
}
const newQp = new qpValidations()
module.exports = newQp;
newQp.addDaystoGetDate();
addDaystoGetDate function required a parameter. try sending a parameter when you call the function.
newQp.addDaystoGetDate(5);
newQp.addDaystoGetDate("some text"); //or whatever you need
Finally got the answer, it was from one of my buddy:), the scope of this.addDaystoGetDate()
gets changed when it is called inside another this.constructDate() function. So as a solution assign "this" object to some variable like below, further can be referred same wherever we come across this
function validation(){
var refObj = this;
refObj.addDaystoGetDate =function(dayscount){
}
refObj.constructDate = function(){
refObj.addDaystoGetDate(2);
}
}
module.exports = new validtaion();

Jasmine-node - Creating a spy on a constructor called inside other function

I'm novice in jasmine and I need to write some unit tests for node.js app in this framework.
I have some problems, one of them is this described below:
var sampleFunction = function(){
var loader = new Loader(params);
// rest of logic here
}
I want to write unit test for sampleFunction. To do this I need to create spy on Loader constructor and check what this constructor gets as params and what kind of object is it returning.
Any ideas how to do that? I tried to create spy on Loader.prototype.constructor but it wasn't a solution to this problem.
OK, so normally in client-side JavaScript you would use the window object like so jasmine.spyOn(window, 'Loader')
In node, however, there is no window object and despite claims to the contrary global is not a substitute (unless you are in the REPL which runs in global scope).
function MyConstructor () {}
console.log(global.MyConstructor); --> undefined
console.log(this.MyConstructor); --> undefined
So, in node you need to attach your constructor to an object. So just do something like this
var Helpers = {
Loader: Loader
};
var constSpy = jasmine.spyOn(Helpers, 'Loader').andCallThrough();
sampleFunction();
expect(constSpy).toHaveBeenCalled();
The andCallThrough call is only necessary if you want your constructor to do something (often with constructor you do).
This is a little hacky, but it works and seems to be the only way to achieve this through jasmine's implementation within node.

How to set variables on the fly from the console?

I would like to set variables on the fly using the console.
My code is wrapped like this:
( function () {
var debug = true;
// code here
} () )
I want to change debug on the fly using the console.
Should I move debug out of the self executing wrapper and pass it in using the global import?
Should I give the anonymous function a name, and set it using the "name spaced" name?
I have not used the console too much, but I assume it is made for things like this.
How is this usually done? What is best practice?
You could use a namespace with minimal effort as follows:
(function (foo) {
foo.debug = true;
}(FOO = FOO || {}));
FOO.debug = false;
I would go with this type of solution over using an explicit global because it isn't really more cumbersome and with variable names like debug there's a chance you might have a conflict.. even if you're working with code that is 100% yours.

Javascript object member function referred to globally not recognized in callback

I'm having a problem with the following Javascript code (Phonegap in Eclipse):
function FileStore(onsuccess, onfail){
//chain of Phonegap File API handlers to get certain directories
function onGetSupportDirectorySuccess(dir){
//stuff
onsuccess();
}
function getDirectory(dir){
return "something" + dir;
}
}
var onFileStoreOpened = function(){
if (window.file_store instanceof FileStore){
console.log('window.file_store is a FileStore');
console.log(window.file_store.getDirectory('something'));
}
}
var onDeviceReady = function(){
window.file_store = new FileStore(onFileStoreOpened, onFileStoreFailure);
}
Here, I want to do some things to initialize file services for the app, and then use them in my initialization from the callback. I get the following error messages in LogCat:
07-03 06:26:54.942: D/CordovaLog(223): file:///android_asset/www/index.html: Line 40 : window.file_store is a FileStore
07-03 06:26:55.053: D/CordovaLog(223): file:///android_asset/www/cordova-1.8.1.js: Line 254 : Error in success callback: File7 = TypeError: Result of expression 'window.file_store.getDirectory' [undefined] is not a function.
After moving the code around and stripping out everything in getDirectory() to make sure it was valid, I'm not even sure I understand the error message, which suggested to me that getDirectory() is not seen as a member function of window.file_store, even though window.file_store is recognized as a FileStore object. That makes no sense to me, so I guess that interpretation is incorrect. Any enlightenment will be greatly appreciated.
I've since tried the following:
window.file_store = {
app_data_dir : null,
Init: function(onsuccess, onfail){
//chain of Phonegap File API handlers to get directories
function onGetSupportDirectorySuccess(dir){
window.file_store.app_data_dir = dir;
console.log("opened dir " + dir.name);
onsuccess();
}
},
GetDirectory : function(){
return window.file_store.app_data_dir; //simplified
}
}
var onFileStoreOpened = function(){
var docs = window.file_store.getDirectory();
console.log('APPDATA: ' + docs.fullPath);
}
var onDeviceReady = function() {
window.file_store.Init(onFileStoreOpened, onFileStoreFailure);
}
and I get
D/CordovaLog(224): file:///android_asset/www/base/device.js: Line 81 : opened dir AppData
D/CordovaLog(224): file:///android_asset/www/cordova-1.8.1.js: Line 254 : Error in success callback: File7 = TypeError: Result of expression 'docs' [null] is not an object.
All I want to do here is make sure certain directories exist (I've removed all but one) when I start, save the directory object for future use, and then retrieve and use it after all initialization is done, and I don't want everything in the global namespace. Of course I would like to be able to use specific instances when necessary, and I'm disturbed that I can't make it work that way since it demonstrates there is a problem with my understanding, but I can't even get this to work with a single, global one. Is this a Javascript problem or a Phonegap problem?
As it stands, your getDirectory function is a private function within FileStore. If you wanted to make it a 'member' or 'property' of FileStore, you would need to alter it a little within FileStore to make it like this:
this.getDirectory = function(dir){ };
or leave it how it is and then set a property....
this.getDirectory = getDirectory();
this way when new FileStore is called it will have getDirectory as a property because the 'this' keyword is always returned when calling a function with 'new'
Hope this quick answer helps. There's lots of stuff on the goog about constructor functions.
You understand it correctly. The getDirectory as it stands is a private function and cannot be called using the file_store instance.
Try this in the browser.
function FileStore(onsuccess, onfail){
function onGetSupportDirectorySuccess(dir){
//stuff
onsuccess();
}
this.getDirectory = function (dir){
return "something" + dir;
}
}
window.file_store = new FileStore('', ''); //the empty strings are just placeholders.
if (window.file_store instanceof FileStore){
console.log('window.file_store is a FileStore');
console.log(window.file_store.getDirectory('something'));
}
This will prove that the basic js code is working fine. If there still is a problem while using it in PhoneGap, comment.

Categories

Resources