Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic require in RequireJS, getting "Module name has not been loaded yet for context" error?

Is there a way to define a module that "dynamically" load other modules in RequireJS? If yes, how the optimizer (r.js) understands how/when a module has to be included?

For example, let dynModules a module which defines name/path pairs:

define([], function () {     return ['moduleA', 'moduleB']; // Array of module names }); 

Another module is going to load modules dynamically, based on the array. This will not work:

define(['dyn_modules'], function (dynModules) {     for(name in dynModules) {            var module = require(path); // Call RequireJS require     }      // ... }); 

... gives me:

Uncaught Error: Module name "moduleA" has not been loaded yet for context: _. Use require([]) http://requirejs.org/docs/errors.html#notloaded

I can solve the error, but it's not "dynamic" anymore:

define(['dyn_modules', 'moduleA', 'moduleB'], function (dynModules) {     for(name in dynModules) {            var module = require(path); // Call RequireJS require     }      // ... }); 
like image 986
gremo Avatar asked Jul 03 '13 11:07

gremo


People also ask

How add require in JavaScript?

To include the Require. js file, you need to add the script tag in the html file. Within the script tag, add the data-main attribute to load the module. This can be taken as the main entry point to your application.

What is RequireJS config JS?

RequireJS is a JavaScript file and module loader. It improves perceived page load times because it allows JavaScript to load in the background. In particular, it enables asynchronous JavaScript loading.

What is define in RequireJS?

The define() function can be used to load the modules (module can be an object, function, class or a code which is executed after loading a module). You can load different versions of the same module in the same page.

Is RequireJS synchronous?

So, RequireJS doesn't support it. From your use case it seems that you don't need synchronous RequireJS, you need to return result asynchronously. AMD pattern allows to define dependencies and load them asynchronously, but module's factory function must return result synchronously.


1 Answers

The limitation relates to the simplified CommonJS syntax vs. the normal callback syntax:

  • http://requirejs.org/docs/whyamd.html#commonjscompat
  • https://github.com/jrburke/requirejs/wiki/Differences-between-the-simplified-CommonJS-wrapper-and-standard-AMD-define

Loading a module is inherently an asynchronous process due to the unknown timing of downloading it. However, RequireJS in emulation of the server-side CommonJS spec tries to give you a simplified syntax. When you do something like this:

var foomodule = require('foo'); // do something with fooModule 

What's happening behind the scenes is that RequireJS is looking at the body of your function code and parsing out that you need 'foo' and loading it prior to your function execution. However, when a variable or anything other than a simple string, such as your example...

var module = require(path); // Call RequireJS require 

...then Require is unable to parse this out and automatically convert it. The solution is to convert to the callback syntax;

var moduleName = 'foo'; require([moduleName], function(fooModule){     // do something with fooModule }) 

Given the above, here is one possible rewrite of your 2nd example to use the standard syntax:

define(['dyn_modules'], function (dynModules) {     require(dynModules, function(){         // use arguments since you don't know how many modules you're getting in the callback         for (var i = 0; i < arguments.length; i++){             var mymodule = arguments[i];             // do something with mymodule...         }     });  }); 

EDIT: From your own answer, I see you're using underscore/lodash, so using _.values and _.object can simplify the looping through arguments array as above.

like image 65
explunit Avatar answered Oct 11 '22 11:10

explunit