Only now I have considered using RequireJS and AMD modules.
So far - all the things have been managed through few global variables and self invoking functions.
Example, for how my module would looke like:
function HugeModule() {
//usage = new HugeModule();
};
HugeModule.prototype.functionX = function() {
//Lets say - around 50 functions for HugeModule prototype
};
HugeModule.SubModule = function() {
//usage = new HugeModule.SubModule();
//And here could be multiple subModules like this
};
HugeModule.SubModule.prototype.functionX = function() {
//Lets say - around 20 functions for HugeModule.SubModule prototype
};
Now I would have written it like this, I would have split it between at least 4 files:
//HugeModule.js
var HugeModule = (function() {
function HugeModule() {
//usage = new HugeModule();
};
return HugeModule;
})();
//HugeModule.somePrototypeFunctions.js
(function() {
HugeModule.prototype.functionX = function() {
//Lets say - around 50 functions for HugeModule prototype
};
})();
//HugeModule.SubModule.js
(function() {
HugeModule.SubModule = function() {
//usage = new HugeModule.SubModule();
//And here could be multiple subModules like this
};
})();
//HugeModule.SubModule.someOtherPrototypeFunctions.js
(function() {
HugeModule.SubModule.prototype.functionX = function() {
//Lets say - around 20 functions for HugeModule.SubModule prototype
};
})();
I would really like to write these modules with AMD modules and RequireJS, I have a basic idea how they should be written, but I am not sure - how would I split them between multiple modules.
I could write it like this:
define([], function() {
function HugeModule() {
//usage = new HugeModule();
};
HugeModule.prototype.functionX = function() {
//Lets say - around 50 functions for HugeModule prototype
};
return HugeModule;
});
but I would like to split it between multiple files. I would prefer not to use build tools that concatenates files.
What I would like is one requirable module - HugeModule and it would resolve all the dependencies for HugeModule.somePrototypeFunctions, and HugeModule.SubModule (and this would resolve dependencie for HugeModule.SubModule.someOtherPrototypeFunctions)
How should I resolve this?
First an important caveat: what you are trying to do does not lend itself well to how ES6 classes work. If you are ever to write ES6 classes or write in a language that has a class syntax similar to ES6 (TypeScript, for instance, has classes that are ES6 + type annotations), you'll run into having to work around the class syntax or run into transpilation problems. Think about refactoring your HugeModule into multiple smaller classes to avoid these problems. (See here for a discussion of the problem in the context of TypeScript.)
If the caveat above is not a concern, you can achieve your goal by organizing your code like the following. I've used this pattern for many years successfully.
HugeModule.js just combines the parts of the class and provide a facade for the rest of the code:
define(["./HugeModuleCore", "./HugeModuleA", "./HugeModuleB"], function (HugeModuleCore) {
return HugeModuleCore;
});
HugeModuleCore.js creates the class and creates some "core" methods on it:
define([], function () {
function HugeModule() {
};
HugeModule.prototype.someCoreFunction = function() {
};
return HugeModule;
});
HugeModuleA.js adds some category of methods to the core:
define(["./HugeModuleCore"], function (HugeModule) {
HugeModule.prototype.someFunction = function() {
};
// You don't really need to return anything here.
});
HugeModuleB.js adds some other category of methods to the core:
define(["./HugeModuleCore"], function (HugeModule) {
HugeModule.prototype.someOtherFunction = function() {
};
// You don't really need to return anything here.
});
I'm having an issue with RequireJS. Essentially, I'm not able to access a function defined inside another file from another one.
I need to do that because I want to export a given subset of functions like
define('submodule', [], function() {
let myFunction1 = function(){ return "Hello"; }
let myFunction2 = function(){ return " From"; }
let myFunction3 = function(){ return " Submodule!"; }
return {
myFunction1 : myFunction1,
myFunction2 : myFunction2,
myFunction3 : myFunction3,
};
});
And accessing them from another file
define('main', ['config', 'sub1', 'sub2', 'submodule'],
function(config, sub1, sub2, submodule) {
//Config
alert(config.conf);
//Submodule
let callSubmodule = function() {
alert(submodule.myFunction1() +
submodule.myFunction2() +
submodule.myFunction3());
}
//sub1
let callSub1 = function() {
alert(sub1.myFunction1());
}
//sub2
let callSub2 = function() {
alert(sub2.myFunction1());
}
});
The fact is that usually I'm able to do this with sub1 and
sub2, but, with submodule, I simply can't. I think it's somehow caused by the dependencies in require.config.js.
My require.config.js:
require(['common'], function () { //contains vendors
require(['config'], function () { //contains a js config file
require(['main'], function () { //main file
require(['sub1', 'sub2'], function () { //some subfiles
require(['submodule']);
});
});
});
});
For submodule.myFunction1() and othe two related functions I'm getting:
Uncaught (in promise) TypeError: Cannot read property 'myFunction1' of undefined
This is weird since I'm able to do that in other situations and I really can't understand why this is happening. For instance, I'm able to call sub1 and sub2 functions from main and other files but not submodule in particular.
Index.html
//Taken from Plunker
. . .
<script data-main="common" data-require="require.js#2.1.20" data-semver="2.1.20" src="http://requirejs.org/docs/release/2.1.20/minified/require.js"></script>
<script src="require.config.js"></script>
. . .
<button onclick = "callSubmodule()">Call Submodule</button>
<button onclick = "callSub1()">Call Sub1</button>
<button onclick = "callSub2()">Call Sub2</button>
common.js contains vendors, here's just an example
requirejs.config({
baseUrl : "",
paths : {
"jquery" : "http://code.jquery.com/jquery-latest.min.js"
}
});
sub1.js
define('sub1', ['submodule'], function(submodule) {
let myFunction1 = function(){ return "called sub1"; }
return {
myFunction1 : myFunction1
};
});
sub2.js
define('sub2', ['submodule'], function(submodule) {
let myFunction1 = function(){ return "called sub2"; }
return {
myFunction1 : myFunction1
};
});
I set up a Plunker with #SergGr help that tries to replicate application's structure but all the modules get undefined on click. On the real application this does not happen.
How can I solve this?
This is your code:
define('main', ['submodule'], function(submod) {
console.log(submodule.myFunction());
});
You have submod in the parameter list. But you then try to access submodule. Note that you return the function straight from your module (return myFunction), so your module has the value of the function myFunction and thus the module is what you should call. The code should be:
define('main', ['submodule'], function(submod) {
console.log(submod());
});
I Managed to solve this issue. Essentially, it was caused by a circular-dependency between the modules. So, a needed b and b needed a leading to one of them being undefined on the dependency resolution.
I found a solution to that on the answer provided by #jgillich at requirejs module is undefined.
So, I managed to solve using, in main
define('main', ['config', 'sub1', 'sub2', 'require'],
function(config, sub1, sub2, submodule, require) {
//Config
alert(config.conf);
//Submodule
let callSubmodule = function() {
alert(require('submodule').myFunction1() +
require('submodule').myFunction2() +
require('submodule').myFunction3());
}
});
As #jgillich said:
If you define a circular dependency ("a" needs "b" and "b" needs "a"), then in this case when "b"'s module function is called, it will get an undefined value for "a". "b" can fetch "a" later after modules have been defined by using the require() method (be sure to specify require as a dependency so the right context is used to look up "a"):
//Inside b.js:
define(["require", "a"],
function(require, a) {
//"a" in this case will be null if "a" also asked for "b",
//a circular dependency.
return function(title) {
return require("a").doSomething();
}
}
);
http://requirejs.org/docs/api.html#circular
The way you've named your modules I would expect they all came from a require config file. I would not expect that requirejs would know how to load those files without some sort of explicit compilation process. I also suspect that your server is returning something due to a 404 that JS is almost able to interpret without exploding.
Your setup seems and naming scheme seems quite strange. If you have the ability to start from scratch below are my recommendations.
Recommendations:
I'm noticing that you're using absolute paths. I highly recommend using relative paths for everything. There are many reasons for this.
Your data-main should be what you call "require.config.js". Your common.js is actually a require.config.js.
You load require.config.js (which is your main) separately using a script tag. You can do this but it's strange.
You can use the "commonjs" style syntax to require files without needing to use the array to define all your dependencies. I recommend that.
This is my recommendation for a set-up:
index.html
<script src="/js/config.js" />
<script src="http://requirejs.org/docs/release/2.1.20/minified/require.js" />
<script>
require('/js/main', function(main) {
main({});
});
</script>
/js/config.js
// setting requirejs to an object before its loaded will cause requirejs to use it as the config
window.requirejs = {
baseUrl : "/",
paths : {
"jquery" : "http://code.jquery.com/jquery-latest.min.js"
}
};
/js/main.js
define(function(require) {
const sum = require('./sum');
return (a, b) => sum(a, b);
});
/js/sum.js
define(function(require) {
return (a, b) => a + b;
});
Update (March 02, 2017)
Your plunker obviously will not work because you have direct calls from HTML to your module functions.
<button onclick = "callSubmodule()">Call Submodule</button>
<button onclick = "callSub1()">Call Sub1</button>
<button onclick = "callSub2()">Call Sub2</button>
RequireJS doesn't work that way. One of key purposes of RequireJS is to provide modules isolation and thus it just can't work that way: imagine if several different modules had functions callSubmodule.
To the best of my knowledge there is no way to bind calls from HTML back to the code in a RequireJS module, it should be other way around: module binds to HTML. And if you fix those issues, everything works fine for me as you can see at this fork of your plunker.
Old Answer
The bug is in your subModule.js
define('submodule', [], function() {
let myFunction = function(){ return "Hello"; }
//return myFunction; // old, wrong
return { myFunction: myFunction };
});
Even if you want to return just 1 function you should not return it as is, you should wrap it into an object and give it an explicit name.
P.S. if this is not your real issuse, please provide us real Minimal, Complete, and Verifiable example
I am going to build an application as it is not too much rich otherwise I can use angularjs for that purpose. I wanted to organize my JS code into proper modular programming approach.
E.g
var SignUpModule = {
elem: $('#id'), // unable to access jquery object here
init: function (jQuery) {
alert(jQuery('.row').html());
}
};
var application = {
modules: [],
addModule: function (module) {
this.modules.push(module);
},
run: function (jQuery) {
_.each(this.modules, function (module) {
//Iterate each module and run init function
module.init(jQuery);
});
}
}
jQuery(document).ready(function () {
application.addModule(SignUpModule);//add module to execute in application
application.run(jQuery);//Bootstrap application
});
Please now look at it I have updated my question with actual code
The biggest mistake you've done is using anonymous first parameter of an anonymous function taken by $.each method. The first argument is just index, and the second argument is an element you were looking for. Below you can find working code.
And no, you don't need to pass jQuery object everywhere. It's global. It already is everywhere. You can see it in the code below.
var SignUpModule = {
elem: $('.row'), //jQuery works fine here
init: function () {
alert(this.elem.html());
}
};
var application = {
modules: [],
addModule: function (module) {
this.modules.push(module);
},
run: function () {
$.each(this.modules, function (index, module) { //this function takes 2 parameters
//Iterate each module and run init function
module.init();
});
}
}
//document.ready
$(function () {
application.addModule(SignUpModule);//add module to execute in application
application.run();//Bootstrap application
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="row">alert <b>me!</b></div>
I have a folder full of JavaScript files:
And within each file is a function definition, e.g.:
function() {
return true;
}
I want to be able to build these files into a single JavaScript object which uses the name and contents of each file under the "foo" folder as property names and values.
So the result of the above would be:
var foo = {
aFunction: function() {
return true;
},
anotherFunction: function() {
return true;
},
yetAnotherFunction: function() {
return true;
}
};
This seems like something that Grunt would be great for, but I can't find any existing plugin that does this, and I'm not sure how to write one that does this myself. How do?
I have a NodeJS file which I browserfied using browserify module. I used following command
browserify testFile.js -o bundle.js
To make use of this file in browser, I am making use of window object.
Assume below code is generated after browserifying the file.
var Main = function () {
this.foo = function () {
},
this.temp= function () {
},
this.bar= function () {
}
}
to make use I changed it to
window.Main = function () {
this.foo = function () {
},
this.temp= function () {
},
this.bar= function () {
}
}
and then to use of these functions, I used following code:
var obj= new Main ();
and then I can say obj.foo(); or obj.bar();
All this is working fine but I wonder if this is the right way to call function from a browserfied file.
Please suggest correct way to make use of browserfied file.
Browserify is a great tool when you use it for the entire project. It makes almost no sense to use it for single files only. The whole point of it is avoiding globals, instead of setting window.Main you could do this:
module.exports = function () {
this.foo = function () {
},
this.temp= function () {
},
this.bar= function () {
}
}
And then in all files that need access to the above, do:
var Main = require('./path/to/main.js');
Browserify resolves and inlines all require calls automatically so you only need to run Browserify on the single file that fires up the app.