Run RequireJS modules before the initialization of RequireJS (data-main)
This article has posted on Stackoverflow
RequireJS does not run data-main script before loading required modules
The problem come up when we run RequireJS modules before the initialization of RequireJS data-main
. The require
has not initialized yet and the RequireJS throw an error. There is no functionality inside the stack of the RequireJS layer that waits the data-main
to be loaded.
This kind of errors can be produced in architectures that are working on a hybrid frontend architecture. For instance, when we load components that contain html/javascript and we inject them on the page. The following code excerpts an example that will possible create the error.
<!DOCTYPE html>
<html>
<head>
<script data-main="js/main.js" src="js/vendor/require.js"></script>
</head>
<body>
<script>
require(['viewmodel/vm', 'ko'],
function(viewmodel, ko) {
ko.applyBindings(viewmodel);
}
);
</script>
</body>
</html>>
Solution
As we mentioned previously, there is no way to handle this issue from RequireJS. Thus, we have to create an additional functionality, isolated from the RequireJS library. The idea is to keep track of our RequireJS components, and execute the components when the data-main
loads. This methodology can be called guard, because it prevents out RequireJS components to execute before the data-main
. Unfortunately, the guard adds a minor boilerplate code on every RequireJS component that we are going to inject on the html.
The implementation is separated in 3 parts.
Use the guard in the components
We have to enclose our component in a function and register it on the guard.
<script>
window.requireJSExecutionGuard.guard(function() {
require(['viewmodel/vm', 'ko'],
function(viewmodel, ko) {
ko.applyBindings(viewmodel);
}
);
});
</script>
Notify the guard that the (data-main) has been loaded
// js/main.js
require.config({
// your config
});
require(['AppLogic'], function( AppLogic ) {
AppLogic.Init();
window.requireJSExecutionGuard.dataMainLoaded();
});
Guard implementation
(function(global) {
var components = [];
var hasLoaded = false;
global.requireJSExecutionGuard = {
guard: guard,
dataMainLoaded: dataMainLoaded
};
function guard(component) {
if (hasLoaded) {
component();
} else {
components.push(component);
}
}
function dataMainLoaded() {
for (var i = 0; i < components.length; ++i) {
components[i]();
}
components = undefined;
hasLoaded = true;
}
})(window);