requirejs plugin loads module in fresh context - javascript

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.

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

The way to use custom method everywhere without requiring the module everywhere

I am using Node.js. I defined a custom method to the String obj like this:
if (!String.prototype.myMethod) {
String.prototype.myMethod= function () {
//do something
return this;
};
}
I found that myMethod maybe used in many different files, so that I have to require the file where this piece of code in. Is there any way that does the many 'requires' ?
Don't do that.
Node is intentionally designed in a module pattern, where each module gets it's own scope to run in and without polluting the global variables. This very intentional and very important.
https://nodejs.org/api/modules.html#modules_the_module_wrapper

access module of other context in requirejs

I am trying to access a togetherJS (https://togetherjs.com/docs/contributing.html) module from an external requireJS app. It seems impossible.
TogetherJSConfig_noAutoStart = true;
var CJS = CJS || {};
CJS.require = require.config({
paths: {
togetherjs: 'https://togetherjs.com/togetherjs-min',
cjs: 'scripts/c'
}
});
CJS.require(['togetherjs'], function() {
// not working
peers = require({context: "togetherjs"})("peers");
// not working
// Module name "peers" has not been loaded yet for context: togetherjs. Use require([])
TogetherJS.require = require.config(TogetherJS.requireConfig);
TogetherJS.require("peers");
});
If I do not use requireJS in my app, I can access the module I want:
// works if I do not use requireJS in my app.
var peers = TogetherJS.require('peers').getAllPeers();
Is it possible, and if so, how? I could not find any information anywhere.
Thanks
I'm not entirely sure of what you are trying to accomplish here. It looks like you are mixing up require.js asynchronous api with node.js synchronous require api.
I think that when you do require.config without specifying a context name, you are configuring the global require context. So calling CJS.require(..) should be equivalent to calling window.require(..).
The line
peers = require({context: "togetherjs"})("peers");
looks strange to me. Usually you would specify the context name in the require.config call and the first parameter to a require(..) call would be the dependency list. Also, you would normally pass a callback function to the require(..) call as well.
I hope those pointers can help you (or others) get better acquainted with require.js

Dojo module loading in general

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.

How do I set a RequireJS context recursively

I'm trying to add Jasmine tests to my Marionette application by creating a specific context for each test containing a map that substitutes all dependencies with mocks except for the module being tested (as described here: http://bocoup.com/weblog/effective-unit-testing-with-amd/).
mockRequire = requirejs.config({
map: {
'*': {
'dep1': '../mocks/dep1'
}
}
});
mockRequire(['appTest'], function(){...})
However since my module appTest has a nested call to require the context is lost:
//appTest.js
define([],
function () {
...
require(['dep1'], function (dep1) {
//Here is not the mocked dep1 but the real one.
})
}
);
So the question is how I can tell requirejs to use the same context for all child calls to require/define or if there are any other ways to solve this.
Change your module definition so that it requires require:
define(['require'], function (require) {
Without it, you use the global require. With it, you use a require that has the proper context. From the documentation:
Note that "require" is specified as a dependency for the module. This allows the require() function that is passed to the function callback to use the right context to load the modules correctly for multiversion support. If "require" is not specified as a dependency, then there will likely be an error.

Categories

Resources