I'm using requirejs in a somewhat special JS environment where an application provides a global singleton (I cannot change this fact, this isn't running in a typical browser environment). I am writing a sort of JS SDK for this application and want to provide various modules that use this global.
Can I wrap that global in a module somehow in order to require it from my modules? Something like
define([the_global_application], function(app)
Thanks for your thoughts on this.
Yes, you just have to define it.
// mysingletonapp.js
// define the module for our global var
define(['list', 'any', 'dependency', 'here'], function (l, a, d, h) {
return yourGlobalVariable;
});
(I don't think you'll have dependencies in there, since you're just wrapping a global var)
The you can use that module as usual:
require(['mysingletonapp'], function (app) {
// do something cool
});
If you want to skip all this, you can use the shim property of RequireJS. You would just need to add this to your options file:
...
shim: {
'globalApplication': {
deps: ['underscore', 'jquery'], // again, you should not need them
exports: 'yourGlobalVar'
}
}
...
shims wrap libraries that don't support AMD, so to have this setting work, you would need a js for globalApplication. This isn't your case.
Related
I have a library - call it SomeLib - which is defined to support various module loaders:
(function(global, factory) {
if (typeof define === 'function' && define.amd) {
define([], factory);
} else if (typeof module !== 'undefined' && module.exports) {
module.exports = factory();
} else {
global.UriTemplate = factory();
}
})(this, function() {
...
// returns constructor function
});
I can easily load it with RequireJS like
require.config({
paths: {
'theLibrary: '../path/to/the/lib'
}
});
Then I have another 3rd-party library - call it AnotherLib - which internally uses SomeLib like
var the Lib = new SomeLib(...);
That means SomeLib has to be available globally.
AnotherLib is just a plain JavaScript module function
(function(){
// the code
})();
It is not compliant with particular module loaders.
When I include AnotherLib with RequireJS, I do something like
require.config({
paths: {
'theLibrary: '../path/to/the/lib',
'anotherLib: '../path/to/anotherLib'
},
shim: {
'anotherLib: [
'theLibrary'
]
}
});
The problem is that I get a undefined exception on the line within AnotherLib where it instantiates SomeLib (new SomeLib(...)).
This is because SomeLib is not defined on the global object but rather published as an AMD module which AnotherLib doesn't "require".
Can I solve this somehow, or does AnotherLib have to be AMD compliant and properly require SomeLib.
The best thing would be to get an AMD-compliant library or to make the library AMD compliant. The latter option would entail modifying the source manually or having a build step that turns the non-AMD-compliant code into a real AMD module. How to do this depends on how the library is designed.
An method that would work with any library is to deliberately leak the symbol that the library requires into the global space:
Make anotherLib be dependent on a new module, which you could call SomeLib-leak.
Create the new module. This definition does not have to be in a separate file. I usually place such "glue" modules before my call to require.config. The module would be like this:
define('SomeLib-leak', ['SomeLib'], function (SomeLib) {
window.SomeLib = SomeLib;
});
I do purposely have define set the module name here. Usually, you don't want to call have define set the module name but for "glue" modules that are placed like I indicated above, this is necessary.
By the time anotherLib is loaded, SomeLibrary will be in the global space.
How do I properly reference modules in the parameters list of Require.js that don't export a variable, since they are bound to the window object? I'm using 'undefined' right now, however, jshint is telling me "'undefined' is already defined.", since two parameters are called 'undefined'.
require(["jquery", "html5shiv", "plugins", "backbone"], function ($, undefined, undefined, backbone) {
// Your code here
});
For dependencies that do not actually expose something (e.g. side-effect only) I place them last as lexeme says in the comment:
require(["realDependency1", "realDependency2", "sideEffectDependency"],
function(realDependency1, realDependency2) {
...
}
);
If the dependency actually provides something, but not through RequireJS's mechanism, you can shim it:
require.config({
...
shim: {
...
"ng": {
exports: "angular"
},
...
}
});
This actually says that whenever I require "ng" (e.g. define(["ng"],...)), the global variable angular (a.k.a. window.angular) will be returned.
I'm using the AMD module pattern and until now, it has been relatively simple to hide what would otherwise be global objects:
define([], function(){
/*jquery here */
var tmp = $;
$ = undefined;
return tmp;
}
However, I'm curious if it's possible to do something similar with google's global objects (I guess they're really into these.. maps and pretty much any of its APIs use em).
Just doing what I've done before actually breaks the code because. It seems internally google is self referencing itself with calls to the global window.google object from scripts it loads on the fly.
I'm going to keep investigating but am curious what you all think!
Thanks.
If you're using RequireJS as your AMD loader, you can use config shims to wrap non-AMD modules, express their dependencies, perform any necessary initializations (where you could clear their global, if the script supports it) and export their global.
For Google Maps, this would look something like this (and no, you probably don't want to clear the global google variable):
require.config({
paths: {
"maps": "https://maps.googleapis.com/maps/api/js?key=API_KEY"
},
shims: {
"maps": {
exports: "google.maps"
}
}
});
Later on, you can use this as a regular AMD module:
require(["maps"], function(maps) {
var map = new maps.Map(document.getElementById("map-canvas"), ....);
...
});
I am using require.js to load my modules which generally works fine. Nevertheless, I do have two additonal questions:
1) If you have a module that is like a helper class and defines additional methods for existing prototypes (such as String.isNullOrEmpty), how would you include them? You want to avoid using the reference to the module.
2) What needs to be changed to use jQuery, too. I understand that jQuery needs to be required but do I also need to pass on $?
Thanks!
1) If you have a module that is like a helper class and defines
additional methods for existing prototypes (such as
String.isNullOrEmpty), how would you include them? You want to avoid
using the reference to the module.
If you need to extend prototypes then just don't return a value and use it as your last argument to require:
// helpers/string.js
define(function() {
String.prototype.isNullOrEmpty = function() {
//
}
});
// main.js
require(['moduleA', 'helpers/string'], function(moduleA) {
});
2) What needs to be changed to use jQuery, too. I understand that
jQuery needs to be required but do I also need to pass on $?
The only requirement for jQuery is that you configure the path correct
require.config({
paths: {
jquery: 'path/to/jquery'
}
});
require(['jquery', 'moduleB'], function($, moduleB) {
// Use $.whatever
});
In my opinion it's unnecessary to use the version of RequireJS that has jQuery built into it as this was primarily used when jQuery didn't support AMD.
Nowadays it does and keeping it separate allows you to swap another library out easily (think Zepto).
2/ For jquery it's really simple :
require(["jquery", "jquery.alpha", "jquery.beta"], function($) {
//the jquery.alpha.js and jquery.beta.js plugins have been loaded.
$(function() {
$('body').alpha().beta();
});
});
More information on require site : http://requirejs.org/docs/jquery.html#get
1/ in my devs for such extension I did it in a global file without require module code.... and I include it in my app with require... not perfect, but it's work fine
global.js
myglobalvar ="";
(...other global stuff...)
myapp.js
// Filename: app.js
define([
(...)
'metrix.globals'
], function(.....){
myApp = {
(...)
I'm trying to load underscore.js with require.js like this:
require(["libs/underscore-1.2.3.js"], function(_) {
...
});
But this doesn't work because underscore.js exports a module name: define('underscore', function() { ... }).
Without renaming lib/underscore-1.2.3.js, how can I load it with require.js?
Alright, after some more googling, I've found: https://github.com/documentcloud/underscore/pull/338#issuecomment-3245213
Where
#dvdotsenko all AMD loaders allow mapping a module ID to a partial path, usually the configuration is called 'paths', so to do what you want:
requirejs.config({
paths:
underscore: 'js/libs/underscore-1.2.3.min'
}
});
require(['underscore'], function () {});
Since underscore is used by other higher-level modules, like backbone, a common dependency name needs to be used to communicate a common dependency on underscore, and it makes sense to call that dependency 'underscore'. The paths config gives a way to do the mapping to a specific URL you want to use for that dependency.
This doesn't answer my question (ie, I still don't know how I'd go about loading underscore if all I had was a URL), but at least it's a functional workaround.
While this doesn't strike me as the most ideal solution, you can require your external files, and then require their registered module names in the inner block.
JSFiddle Example
require(
['require','http://documentcloud.github.com/underscore/underscore-min.js'],
function(require){
require(['underscore'],function(_){
var a = _.intersection([1,2,3],[2,3,4]);
document.write("Underscore is available in the closure : " + a);
})
}
)
It might not look pretty, but that might be a recommended pattern for loading up initial assets so that they can be required intuitively in dependent modules.