Dojo module loading in general - javascript

Coming from Java/C# I struggle to understand what that actually means for me as a developer (I'm thinking too much object-oriented).
Suppose there are two html files that use the same Dojo module via require() in a script tag like so:
<script type="text/javascript">
require(["some/module"],
function(someModule) {
...
}
);
</script>
I understand that require() loads all the given modules before calling the callback method. But, is the module loaded for each and every require() where it is defined? So in the sample above is some/module loaded once and then shared between the two HTMLs or is it loaded twice (i.e. loaded for every require where it is listed in the requirements list)?
If the module is loaded only once, can I share information between the two callbacks then? In case it is loaded twice, how can I share information between those two callbacks then?
The official documentation says "The global function define allows you to register a module with the loader. ". Does that mean that defines are something like static classes/methods?

If you load the module twice in the same window, it will only load the module once and return the same object when you request it a second time.
So, if you're having two seperate pages, then it will have two windows which will mean that it will load the module two times. If you want to share information, you will have to store it somewhere (the web is stateless), you could use a back-end service + database, or you could use the HTML5 localStorage API or the IndexedDB (for example).
If you don't want that, you can always use single page applications. This means that you will load multiple pages in one window using JavaScript (asynchronous pages).
About your last question... with define() you define modules. A module can be a simple object (which would be similar to static classes since you don't have to instantiate), but a module can also be a prototype, which means you will be able to create instances, for example:
define([], function() {
return {
"foo": function() {
console.log("bar");
}
};
});
This will return the same single object every time you need it. You can see it as a static class or a singleton. If you require it twice, then it will return the same object.
However, you could also write something like this:
define([], function() {
return function() {
this.foo = function() {
console.log("bar");
};
};
});
Which means that you're returning a prototype. Using it requires you to instantiate it, for example:
require(["my/Module"], function(Module) {
new Module().foo();
});
Prototyping is a basic feature of JavaScript, but in Dojo there's a module that does that for you, called dojo/_base/declare. You will often see things like this:
define(["dojo/_base/declare"], function(declare) {
return declare([], {
foo: function() {
console.log("bar");
}
});
});
In this case, you will have to load it similarly to a prototype (using the new keyword).
You can find a demo of all this on Plunker.
You might ask, how can you tell the difference between a singleton/static class module, and a prototypal module... well, there's a common naming convention to it. When your module starts with a capital letter (for example dijit/layout/ContentPane, dojo/dnd/Moveable, ...) then it usually means the module requires instances. When the module starts with a lowercase letter, it's a static class/singleton (for example dojo/_base/declare, dijit/registry)

1) dojo require, loads the module once and then if you called it again require() will simply return if the package is already loaded. so the request will be called once and it will also call any dependencies once.
but all that if you are in the same HTML page if you leave the page and call the same module in a different page then it will be called from the server. you can also use cache in your config settings so things will be cached in the browser and the file will or not by setting the cacheBust to true if you want a fresh copy or false if you want things to be cached.
2) if you are in the same html page and domain, the module didn't change the module will be the same and you can share values and any change you make you can get it from anywhere unless you call a new instance. but that is not possible between different html pages.
3) not it is not like a static classes or methods from what I understand static methods A static class can be used as a convenient container for sets of methods that just operate on input parameters and do not have to get or set any internal instance fields..
dojo work differently it is a reference for an object if you did it in that way :
define(function(){
var privateValue = 0;
return {
increment: function(){
privateValue++;
},
decrement: function(){
privateValue--;
},
getValue: function(){
return privateValue;
}
};
});
This means every bit of code loads that module will reference the same object in memory so the value will be the same through out the use of that module.
of course that is my understanding please feel free to tell me where I am wrong.

Related

Replacing requirejs with systemjs -- variables not visible in local scope

I'm trying to convert our requirejs calls to use SystemJS, but I'm not exactly sure what I'm doing wrong.
Our original calls look like this:
return function(callback) {
requirejs(["/app/shared.js"], function(result){
callbackFunction = callback;
callback(dashboard);
main();
})
}
And what I'm trying instead is:
return function(callback) {
console.log(callback.toString())
SystemJS.import('app/shared.js').then(function(result){
callbackFunction = callback;
callback(dashboard);
main();
});
}
I've had to remove some leading / to get things to load properly, which is fine, but I've now ran into an issue where variables that were defined at the top of shared.js aren't visible in my local main.js file. In my browser console I get:
Potentially unhandled rejection [1] ReferenceError: dashboard is not defined
shared.js defines dashboard:
var dashboard = { rows: [], }
// Other definitions...
define(["/app/custom-config.js", /* etc */]);
I guess I have two questions:
is this the correct way to replace requirejs calls?
if so, why aren't my variables from shared.js accessible?
For a fuller picture, main() just sets up the dashboard object, and then calls callbackFunction(dashboard) on it.
Your problem can be reduced to the following case where you have two AMD modules, with one that leaks into the global space, and the 2nd one that tries to use what the first one leaked. Like the two following modules.
src/a.js requires the module that leaks and depends on what that module leaks:
define(["./b"], function () {
console.log("a loaded");
callback();
});
src/b.js leaks into the global space:
// This leaks `callback` into the global space.
var callback = function () {
console.log("callback called");
}
define(["./b"], function () {
console.log("b loaded");
});
With RequireJS, the code above will work. Oh, it is badly designed because b.js should not leak into the global space, but it will work. You'll see callback called on the console.
With SystemJS, the code above won't work. Why? RequireJS loads modules by adding a script element to the header and lets script execute the module's code so callback does end up in the global space in exactly the same way it would if you had written your own script element with an src attribute that points to your script. (You'd get an "Mismatched anonymous define" error, but that's a separate issue that need not detain us here.) SystemJS, by default, uses eval rather than create script elements, and this changes how the code is evaluated. Usually, it does not matter, but sometimes it does. In the case at hand here callback does not end up in the global space, and module a fails.
Ultimately, your AMD modules should be written so that they don't use the global space to pass information from one another.
However, there is another solution which may be useful as a stepping-stone towards a final solution. You can use scriptLoad: true to tell SystemJS to use script elements like RequirejS does. (See the documentation on meta for details and caveats.) Here is a configuration that does that:
System.config({
baseURL: "src",
meta: {
"*": {
scriptLoad: true, // This is what fixes the issue.
}
},
packages: {
// Yes, this empty package does something. It makes `.js` the
// default extension for modules.
"": {}
},
});
// We have to put `define` in the global space to
// so that our modules can find it.
window.define = System.amdDefine;
If I run the example code I've given here without scriptLoad: true, then module a cannot call the callback. With scriptLoad: true, it can call the callback and I get on the console:
b loaded
a loaded
callback called

Require JS callback argument undefined

First off, I'm pretty new to Require JS, and I haven't done my fair share of reading the docs. Kind of just shooting from the hip here.
But this is functionality that, should work. Well as far as I've read.
I have a hashed URL, say at this stage it's #index.
And then I have the equivalent js page under /javascript/pages/index.js.
As you would figure, I'm trying to load these pages "dynamically".
However, my callback functions page parameter is undefined.
require(['javascript/pages/' + page],
function(page) {
var constructedPage = new page();
});
All Pages are "classes" function index(){}
In the meanwhile, I'll start reading up on the docs a bit more.
If you want to use objects/variables/etc created in the index.js within the callback of require(), you have to use a define() call to specify that object.
index.js
define(function(){
// create an object with constructor
function myPage(){
}
// some more code adding to the prototype
// return the actual object
return myPage;
});
Then you can use that object like you did in your code.
Note: That define() call may have dependencies of its own. Omitted here for simplicity's sake.

javascript linking

I'm going to build a rather complicated application in html5 with some heavy javascripting.
In this I need to make some objects which can be pass around. But since there is no like #import foobar.js.
Then I assume that if my html page loads the scripts, then all the scripts can access eachother?
I read (here) that ajax somehow is able to load a .js file from within another .js file. But i dont think this is what I need?
Can go more into details if needed, thanks in advance.
In our projects, we do the following: Suppose you're going to call your project Foo, and have a module called Bar in it,
Then what we do is declare a file called Foo.js that just defines an equivalent to a Foo namespace:
Foo = (function(){
return {
};
})();
Then we create a file called Foo.Bar.js that contains the code for the Bar module:
Foo.Bar = (function(){
// var declarations here that should be invisible outside Foo.Bar
var p, q;
return {
fun1 : function(a, b){
// Code for fun1 here
},
fun2 : function(c) {
// Code for fun2 here
}
} // return
})();
Note that how it is a function that executes immediately, and returns an object that gets assigned to Foo.Bar. Any local variables, like p and q are available to fun1 and fun2 because they're in a closure, but they are invisible outside of Foo.Bar
The functions in Foo.Bar can be constructors for objects and so on.
Now in your HTML you simple include both files like so:
<script type="text/javascript" src="Foo.js"></script>
<script type="text/javascript" src="Foo.Bar.js"></script>
The result will be that you can call Foo.Bar's functions in the JavaScript of your main HTML file without any problems.
You should check out the module pattern:
http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth/
This describes alternatives for creating modular code in javascript, how you can protect your code and share APIs and data among them.
You should also consider using and AMD. Require.js is quite popular, but I tend to prefer head.js for this. Keep in mind that these put some requirements on how you structur your code in files, and personally, I don't think it's worth it, compared to a concatenated and minified file included in the bottom of the page.
Then I assume that if my html page loads the scripts, then all the scripts can access eachother?
Yes.

requirejs plugin loads module in fresh context

I want to have a module load multiple times on a page, with a unique context for each instance. There are two normal ways to do this.
the module can return a constructor
i can explicitly run the require method, providing a context id.
Either of these would work in my situation, but I want a third option. :)
I want a requirejs plugin that will return a module to me in a fresh context. ie,
require(["new!some/module"], function(SomeModule) {
// SomeModule, here, is in its own context. If i were to run this
// method call again, SomeModule would be in a new context.
});
I've started looking into building a plugin to do this...
load: function (name, req, load, config) {
var index = get_unique_index();
req({context:index}, [name], function(value) {
if(!config.isBuild){
load(value);
}
});
}
but the {context:index} dict doesn't work here... thoughts?
Instead of using the local req passed to load, use the global require function, just called require(). The local one passed to load is already bound to a context.

Javascript Modular Layout : How to call a function defined in one module from another?

Below is an example of a modular layout of a javascript application. I want to start using such a structure for my work. I am struggling to get my head round how it works and need to understand how to call a function that is defined in one module from a different module? Is this definitely the bet way to layout a JavaScript heavy application?
window.MainModule = (function($, win, doc, undefined) {
var modules = {};
// -- Create as many modules as you need ...
modules["alerter"] = (function(){
var someFunction = function(){ alert('I alert first'); };
return {
init: someFunction
};
}());
modules["alerter2"] = (function(){
var someFunction = function(){ alert('I alert second'); };
return {
init: someFunction
};
}());
return {
init: function(){
for (var key in modules){
modules[key].init();
}
}
};
}(jQuery, this, document));
$(window.MainModule.init);
Modularity with RequireJS:
module1.js
define( ["dependency"] , function( dep ){
return {
speak: function(){
alert( dep.message );
}
}
} );
dependency.js
define( {
message: "Hello world!"
} );
impl.js
if ( $(".someCssExpression").length ) {
require( [ "module1" ] , function( mod ){
mod.speak();
});
}
index.html
...
<script src="require.js" data-main="impl"></script>
...
Your file structure will be modular.
Your implementation will be modular.
And no clunky namespacing or weird constructs to make it feel organised.
Takes some getting used to, but totally worth it.
Also read:
ScriptJunkie article
In order to access anything it needs to be available in the scope from where you are calling. "Module" - or any capsulation method for that matter - in JS always means "function". A module is just an anonymous (unnamed) function. So to access an element defined in another function B(module) from inside function A it either has to be made available in GLOBAL SCOPE (in browsers: the window object), OR it must have obtained access some other way, e.g. by having received a reference through some function call. YUI3 ([http://developer.yahoo.com/yui/3/]) is an interesting example for the latter, there nothing of your application ever is available in global scope (I consider YUI3 one of the by far best JS frameworks for SERIOUS softwarfe development, also definitely DO check out http://developer.yahoo.com/yui/theater/, especially any videos from Douglas Crockford, a Javascript God (and I'm not usually given to giving these kinds of statements).
What you have to keep in mind with Javascript is that part of what in languages such as C is done by the compiler now happens at runtime. For such things like immediate function invocations that return a function (causing encapsulation through usage of closures) you should remember that that code runs exactly ONCE DURING LOADING, but during runtime completely different code is executed - which depends on what the on-load code execution did.
In your example the function after window.MainModule=... is executed on loading of the JS code. Note that window.MainModule does NOT POINT TO THAT FUNCTION!!!
Instead, that function is, as I said, executed on load, and the RESULT is assigned to window.MainModule. What is the result? Well, there is just one return statement, and it returns and object, and the object has just one property "init" which points to an anonymous function.
Before returning that object, though, that function creates a variable "modules" in its local scope, which points to an object. The object has two properties, and those properties are assigned functions the same way that window.MainModule is assigned one, so you have three closures all in all.
So after loading you have one global variable
window.MainModule = {
init: function(){...}
So after loading you have one global variable
window.MainModule = {
init: function(){...}
}
In the last line that function is executed.
}
In the last line that function is executed.
The example does not make a lot of sense though, because you don't really encapsulate anything. You make available the private function with double pointers: from init to the local variable someFunction, nothing is hidden. Check out the above URLs (Yahoo Developer Theater) for better examples and very thorough explanations. It is MUST-WATCH even if you never touch YUI3 - especially the videos from D. Crockford are general JS knowhow.
Well umm..
It all depands on what you need from your application.
I would suggest sticking to the jQuery plugins for as long as you touch the gui.
You could use the Namespace pattern like yahoo, and just forge a great framework for your
application.
You probably don't need your modules to run on every page, like you did when instantiating the main module (There is no point to it unless f.e. you have a widget in every page on your website).
After you finish to abstract all the actions you need from your javascript into function and modules, build a module that will load the logic according to each page/action/whatever whenever you need it.
By the way, You can always develop OO style using mootools and this is endless really. It all boils down to your application needs
I would really recommend you to watch some lectures of Douglas Crockford(As been stated before me) and here is a nice article about modules that may help you understand it a bit further. Good luck!

Categories

Resources