I'm having a bit of trouble with contexts in requireJS. What I'd like to is create a context, "mycontext", at the config stage (before I load any modules), and then have that context kept throughout. This is complicated because I am unfortunately required (<- ha!) to use the CommonJS syntax for my modules. So, if this is my base file looks like this:
base.js
contextReq = require.config({
context: 'mycontext',
baseUrl: 'http://www.example.com/src/',
paths:{
jquery: 'http://ajax.cdnjs.com/ajax/libs/jquery/2.0.3/jquery.min',
},
});
(function(){
contextReq(['require', 'topModule'], function(require, topModule){
topModule.initialize();
});
})();
Then, I load topModule:
http://www.example.com/src/topModule.js
define(['require', 'jquery', 'nestedModule'], function (require) {
var $ = require('jquery');
var other = require('nestedModule');
});
Will jQuery still be loaded only in mycontext? What if I go a level further:
http://www.example.com/src/nestedModule.js
define(function (require) {
var $ = require('jquery');
var oneMore = require('someOtherModule');
});
We already have access to jquery in this context, but will "someOtherModule" also be loaded in this context, or in the global "_" context? Is there any way to check if a module is already loaded before I make the require call?
Thanks!
Ok, so I figured this out myself. Require, locally or globally, has a very useful property called ".s" which lists, among other things, all of requires contexts. I ran "require.s.contexts" on to the console after my require has finished loading:
base.js
contextReq = require.config({
context: 'mycontext',
baseUrl: 'http://www.example.com/src/',
paths:{
jquery: 'http://ajax.cdnjs.com/ajax/libs/jquery/2.0.3/jquery.min',
},
});
(function(){
contextReq(['require', 'topModule'], function(require, topModule){
topModule.initialize();
});
})();
//Timeout, then see where we stand
setTimeout( function () {
console.log(require.s.contexts._);
console.log(require.s.contexts.mycontext);
}, 500);
The output was as follows:
//Console.log for context '_' (the default require context)
{
[...]
defined: [], //empty array, nothing has been loaded in the default context
[...]
}
//Console.log for context 'mycontext' (the default require context)
{
[...]
defined: [ //Filled out array; everything is loaded in context!
topModule: Object
nestedModule: Object
jquery: function (e,n){return new x.fn.init(e,n,t)} //jQuery function
],
[...]
}
So, in summary, my hunch was correct: when a top level requireJS module is loaded in a particular context, all modules loaded from within that top level module are loaded in context, even if the context is no longer specified.
Related
I have two Javascript file loading from the same domain. Each JS performing different function and for that, I have used requirejs module loader.
My problem is, I have used require.config call under both js and baseUrl is different for both configuration.
JS1
require.config({
baseUrl : server + "js/", // server is 192.168.1.10:3000/
paths :
{
jquery : "jquery",
moment : "moment.min"
},
waitSeconds: 20
});
require(["jquery","moment"], function(jQuery,moment)
{
jQuery.noConflict();
window.moment = moment;
jQuery(function($)
{
window.$191 = $;
callback($191);
});
});
JS2
require.config({
baseUrl: "http://192.168.1.9:6060/App/resources",
paths:
{
moment: "js/moment.min",
ejs : "js/ejs"
}
});
require(["moment","ejs"],function()
{
callback("OK");
});
I am loading JS1 first followed by JS2, but when I am loading JS2 its baseUrl change by 1st one i.e. from JS1 (server+"js/") !! Which is totally wrong. Can some one point me out here, if something I have done wrong.
Nikos Paraskevopoulos has the right idea for one way (multiversion) to approach this, but I think there are two ways.
1. Using multiversion
Passing two configs to require.config() will return to you two unique require functions.
// It is important to give the config objects a "context" name.
// This is what keeps them unique.
var r1 = require.config({
context: "r1",
baseUrl: "//rawgithub.com/requirejs/text/2.0.10/"
});
var r2 = require.config({
context: "r2",
baseUrl: "//cdnjs.cloudflare.com/ajax/libs/require-text/2.0.10/"
});
In addition, even if you used the same baseUrl in each config in the sample above, each text module would be a unique instance because of the nature of multiversion support.
These can be used to load modules from different URLs. In the example below, text1 and text2 are different module instances, even though they are essentially the same file.
r1(["require", "text"], function(r, text1) {
// ...
});
r2(["require", "text"], function(r, text2) {
// ...
});
The "require" module is required as a dependency if you use the var myModule = require("..."); syntax within the module body.
If all your dependencies are declared in the dependency array then you do not need this.
(There is a simple example of this at this jsfiddle)
But back to your example, you can assign local require functions to each file, and load modules via that local require function.
JS1
(function () {
var r = require.config({
context: "JS1",
baseUrl: server + "js/", // server is 192.168.1.10:3000/
paths: {
jquery: "jquery",
moment: "moment.min"
},
waitSeconds: 20
});
r(["jquery", "moment"], function (jQuery, moment) {
jQuery.noConflict();
window.moment = moment;
jQuery(function ($) {
window.$191 = $;
callback($191);
});
});
}());
JS2
(function () {
var r = require.config({
context: "JS2",
baseUrl: "http://192.168.1.9:6060/App/resources/",
paths: {
moment: "js/moment.min",
ejs: "js/ejs"
}
});
r(["moment", "ejs"], function () {
callback("OK");
});
}());
Aside
It's unusual for modules to not start with a call to the define() function, however. Usually, config and the modules are in separate files. So you might have:
JS1-config.js and JS1.js
JS2-config.js and JS2.js
And JS1.js and JS2.js would be of the form:
define(["array", "of", "dependencies"], function(array_, of_, dependencies_) {
});
2. Using a consolidated require config
A cohesive application is probably better served with a single module loader configuration file. You have visibility on all the required modules, and it'll probably play better with the r.js optimiser, should you want to use that.
As such, I would recommend consolidating your configs into a single config for your application. This way you don't mix the module loader config with the code that actually uses the modules.
If you can't modify some of the code, then this could be tricky to do though.
E.g.:
var baseUrl1 = server + "js/";
var baseUrl2 = "http://192.168.1.9:6060/App/resources/";
require.config({
paths: {
jquery: baseUrl1 + "jquery",
moment: baseUrl1 + "moment.min",
//moment: baseUrl2 + "js/moment.min",
ejs: baseUrl2 + "js/ejs"
},
waitSeconds: 20
});
This assumes that both JS files can use the same "moment" module.
I am trying to test a simple node Module with intern.The module is an AMD module. I am running into a couple of issues
If the module is defined as below, I get the error "moduleName" has
no method 'map' "
define('moduleName',[]function(require){ var r= require('request');
})
If the module is defined as below without a moduleName, I see this
error "undefined is not a function" - I guess its not able to resolve 'require'
define([]function(require){ var r= require('request'); })
Here is how my Test Looks
define([
'intern!object',
'intern/chai!assert',
'/src/api/nameApi'
], function (registerSuite, assert,nameApi) {
registerSuite({
name: 'GetName Test',
getName: function () {
var nameFromApi = nameApi.getName();
assert( nameFromApi!= null,'name is not null');
}
});
});
Providing an explicit module ID as the first argument to define destroys module portability and shouldn’t be done. It is not currently supported by the loader used by the master branch because it is such a bad idea. It is supported by the geezer branch, but again, I strongly advise you to never use this pattern.
The second module definition you have provided is just wrong; you need to put 'require' in your dependencies array if you expect to load the special require function. You also can’t use a variable to pass a module ID to the require function if you are expecting it to be pre-loaded as a dependency. So, it should look like this:
define(['require', 'foo'], function (require) {
var foo = require('foo');
});
or, using the CommonJS compatibility wrapper syntax, where require is implicitly provided:
define(function (require) {
var r = require('foo');
});
EDIT: Also, now that you have added the test module: within your test’s define, '/src/api/nameApi' is not a valid AMD module identifier.
When I try to initialize an object declared/defined in a different file (but I believe it's been loaded via requireJS), it gives me ReferenceError: myTemplates is not defined. I have two files: main.js and templates.js.
In main.js (which is where I would like to load the object from templates.js and initialize it,
define(["jquery-ui", "templates"], function () {
var templates = new myTemplates(); // gives ReferenceError
alert( "Never reached this call" + $().jquery);
$(document).ready(function() {
alert( "Never reached this call " + $().jquery);
});
});
In templates.js, which just have an object named myTemplates with a function named test as follows
define(["jquery-ui"], function () {
alert( "This alert is raised, proving that jQuery/jQuery UI is loaded in templates.js " + $().jquery);
function myTemplates(){
this.test = function(){
alert('test');
return false;
};
};
});
For the config file for requireJS, named requireconfig.js, I have
requirejs.config({
"baseUrl": "scripts/lib",
"paths": {
"app": "../app",
"jquery" : [
"//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min",
"jquery-1.10.2.min"],
"jquery-ui" : [
"//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min",
"jquery-ui-1.10.3.min"],
"templates" : "../app/templates/templates"
},
shim: {
"jquery-ui": {
exports: "$",
deps: ["jquery"]},
}
});
requirejs(["app/main", "templates"]);
I'm pretty sure the jQuery and jQuery UIs are loaded properly in templates.js, but I can't understand why I'm getting ReferenceError for initializing myTemplates in main.js. Could anyone please take a look and help me out. Thank you.
p.s. The Github upload of the files/folders is here in case anyone wants to look at the folder/file hierarchy.
OK, two problems
in templates.js, once you defined a object, or function, you have to return it
function myTemplates(){ ... };
return myTemplates;
in main.js, you have to give a reference name for those defined objects, unless they are not AMD or defined in shim config.
define(["jquery-ui", "templates"], function ($ui, myTemplates)
give a try!
I notice in the documentation there is a way to pass custom configuration into a module:
requirejs.config({
baseUrl: './js',
paths: {
jquery: 'libs/jquery-1.9.1',
jqueryui: 'libs/jquery-ui-1.9.2'
},
config: {
'baz': {
color: 'blue'
}
}
});
Which you can then access from the module:
define(['module'], function (module) {
var color = module.config().color; // 'blue'
});
But is there also a way to access the top-level paths configuration, something like this?
define(['module', 'require'], function (module, require) {
console.log( module.paths() ); // no method paths()
console.log( require.paths() ); // no method paths()
});
FYI, this is not for a production site. I'm trying to wire together some odd debug/config code inside a QUnit test page. I want to enumerate which module names have a custom path defined. This question touched on the issue but only lets me query known modules, not enumerate them.
It is available, but it's an implementation detail that shouldn't be depended on in production code ( which you've already said it's not for, but fair warning to others! )
The config for the main context is available at require.s.contexts._.config. Other configurations will also hang off of that contexts property with whatever name you associated with it.
I don't believe require exposes that anywhere, at least I can't find it looking through the immense codebase. There are two ways you could achieve this though. The first and most obvious is to define the config as a global variable. The second, and closer to what you want, is to create a require plugin that overrides the load function to attach the config to the module:
define({
load: function (name, req, onload, config) {
req([name], function (value) {
value.requireConfig = config;
onload(value);
});
}
});
I'm fairly new to RequireJS and I've run into a bit of a problem. I've written a little framework built on Backbone using RequireJS and I want it to be re-usable in different projects. So, with some searching I learned that require allows packages. This seemed like what I was looking for. I have a main.js file to launch my app that essentially looks like this:
require.config({
packages: ['framework']
});
require(['framework'], function(framework) {
framework.createDash();
});
Then in the same directory as my main.js I have another directory called "framework" which contains another main.js which looks like this:
define(function(require, exports, module) {
exports.createDash = function(dash, element) {
require(['dash/dash.model', 'dash/dash.view'], function(DashModel, DashView) {
return new DashView({
model: new DashModel(dash),
el: element ? element : window
});
});
};
});
In searching I found this page which indicates that the 'require' argument should be scoped to the submodule. However, when I try to require things they are still relative to my original main.js. I've tried a number of things and searched for hours to no avail. Is there any way I can have my require/define calls within my package included relative to the main.js in it's root?
You need to define your submodule as package in the require configuration:
require.config({
packages: [
{ name: 'packagename',
location: 'path/to/your/package/root', // default 'packagename'
main: 'scriptfileToLoad' // default 'main'
}]
... some other stuff ...
});
To load your module you just need to use your 'packagename' at the requirements:
define(['jquery', 'packagename'], function($, MyPackage) {
MyPackage.useIt()
});
In your package you must use the ./ prefix to load your files relative to your submodule:
define(['globalDependency', './myLocalFile'], function(Asdf, LocalFile) {
LocalFile.finallyLoaded();
});
There is a useful shortcut: If your package name equals to your location and your main file is called 'main.js', then you can replace this
packages: [
{ name: 'packagename',
location: 'packagename',
main: 'main'
}]
to this:
packages: ['packagename']
As far as I can see, you already tried to define a package but did you also use the ./ prefix? Without this prefix require will try to find the files in it's global root-path. And without a package, ./ will be useless because the relative path is the same as the global root-path.
Cheers
I figured out the answer to my question, and the solution (they were not the same apparently). I guess I'll post it here in case it can help someone else in the future.
Essentially what I was wanting was to load my framework within its own context. I found the context option under the configuration section on require's website and an example of how to use it. Originally I tried this by doing something like:
var req = require.config({
baseUrl: 'framework',
context: 'framework',
paths: {
jQuery: 'lib/jquery/jquery-1.7.min.js',
Underscore: 'lib/underscore/underscore.min.js',
Backbone: 'lib/backbone/backbone.min.js',
etc...
}
});
req(['main'], function() {});
There were two problems with this. First, my 'req' variable was being defined outside of the framework, but I wanted the framework to define it's own paths. And second, whenever a file outside of the framework would require a file within the framework, which would in turn require 'jQuery', for example, then jQuery (or whatever else) wouldn't be required from within the context of the framework instance of require and so it couldn't find the file.
What I ended up doing was defining my framework's main.js to look something like this:
var paths = {
jQuery: 'lib/jquery/jquery-1.7.min.js',
Underscore: 'lib/underscore/underscore.min.js',
Backbone: 'lib/backbone/backbone.min.js',
etc...
};
define(function() {
var exports = {};
exports.initialize = function(baseUrl, overridePaths, callback) {
if(!overridePaths) {
overridePaths = {};
}
if(baseUrl && baseUrl[baseUrl.length - 1] != '/') {
baseUrl = baseUrl + '/';
}
var fullpaths = {};
for(var path in paths) {
// Don't add baseUrl to anything that looks like a full URL like 'http://...' or anything that begins with a forward slash
if(paths[path].match(/^(?:.*:\/\/|\/)/)) {
fullpaths[path] = paths[path];
}
else {
fullpaths[path] = baseUrl + paths[path];
}
}
var config = {paths: fullpaths};
for(var pathName in overridePaths) {
config.paths[pathName] = overridePaths[pathName];
}
require.config(config);
// Do anything else you need to do such as defining more functions for exports
if(callback) {
callback();
}
}
return exports;
});
And then in my project's main.js file I just do this:
require(['framework/main'], function(framework) {
// NOTE: This setTimeout() call is used because, for whatever reason, if you make
// a 'require' call in here or in the framework without it, it will just hang
// and never actually go fetch the files in the browser. There's probably a
// better way to handle this, but I don't know what it is.
setTimeout(function() {
framework.initialize('framework', null, function() {
// Do stuff here
}
}, 0);
});
This takes whatever is passed in to the framework's initialize() method for 'baseURL' and prepends that to any paths that the framework defines that do not start with a forward slash or 'anything://', unless they are override paths. This allows the package using the framework to override things like 'jQuery'.
This worked for me, adding a "./" prefix to the module names:
define(function (require, exports, module) {
exports.createDash = function (dash, element) {
require([ './dash/dash.model', './dash/dash.view' ], function (DashModel, DashView) {
return new DashView({
model : new DashModel(dash),
el : element ? element : window
});
});
};
});
A process that worked well for me for allowing a package with submodules to be used directly from data-main or from an outside framework, assuming that a main.js (or other package main) is called by a particular name, was to use var baseUrl = require.toUrl('packageName') + '/../' as a prefix to a require.config({ paths: { ... } }) configuration file. For instance:
var music21Base = require.toUrl('music21') + '/../';
require.config({ paths: {
'jquery': music21Base + 'ext/jquery/jquery.2.1.10.min';
'subModuleLoader': music21Base + 'src/subModuleLoader';
} });
The setting of context: "xxx" worked fine for calling normal modules with ./modName but did not work for the paths argument for me.