How to solve circular dependency in Require.js? - javascript

Basically, the idea is that "sub" module creates an object, and that object should be part of a utilities library which is the "main" module. However, the "sub" object depends on utilities from "main":
// Main module
define(['sub'], function(sub) {
var utils = {
utilityMain: function () {
// ...
};
// ...
};
tools.subModule = sub;
return tools;
});
// Sub module
define(['main'], function(main) {
return new (function () {
// Singleton object using functions in main module
var somestuff = function () {
main.utilityMain();
// etc
};
})();
});
How can I achieve this with require.js without creating a black hole that would swallow the whole planet?
Thank you very much.

There are a few things suggested in the docs:
b can fetch a later after modules have been defined by using the require() method (be sure to specify require as a dependency so the right context is used to look up a)
e.g.:
// Sub module
define(['require'], function(require) {
return new (function () {
// Singleton object using functions in main module
var somestuff = function () {
require('main').utilityMain();
// etc
};
})();
});
or
you could instead use exports to create an empty object for the module that is available immediately for reference by other modules
e.g.:
// Main module
define(['sub', 'exports'], function(sub, exports) {
exports.utilityMain: function () {
// ...
};
exports.subModule = sub.sub;
});
// Sub module
define(['main', 'exports'], function(main, exports) {
exports.sub = new (function () {
// Singleton object using functions in main module
var somestuff = function () {
main.utilityMain();
// etc
};
})();
});
and
Circular dependencies are rare, and usually a sign that you might want to rethink the design

Related

protractor calling an exported function within the same module

I'm newer to using Protractor for automation, so forgive me if this ends up being a dumb question. I have a helper.js module with a bunch of functions that I or other team members can use. One of the functions from helper.js needs to call to one of the existing functions in the module.
Is this possible? I have tried several different ways to do this and so far none have worked other than to break the helper functions into a separate js file that I need to call to.
Example:
helper.js:
module.exports = {
newbrowsertab: function(){
<code>
},
anotherfunction: function(){
<code>
<call to newbrowsertab();>
<code>
},
anotherfunction2: function(){
<code>
}
};
In the call to the newbrowsertab function, I've tried:
module.newbrowsertab();
this.newbrowsertab();
self.newbrowsertab();
You could use Prototypal inheritance then:
// helper.js functions
// create object
var Util = function() {};
// extend object
Util.prototype.enterPassword = function() {
// code
};
// extend object
Util.prototype.clickLogin = function() {
// code
};
// use `this` to call functions in same module
Util.prototype.fullLogin = function() { // extend object
this.enterPassword();
this.clickLogin();
};
module.exports = new Util();
Then in your test file:
var Util = require('./path/to/helper.js);
Util.fullLogin();
etc...
Expanding on the prototypal convention.
Any functions that are only helpers for other exported functions could be named with an underscore and declared to be executed later.
function _helperFunction(){
// do something
// return something
}
var exposedFunction = function() {
// do something
var x = _helperFunction();
// Do something else
}
module.exports = {
exposedFunction : exposedFunction
};

Expose AMD module globally when not in a RequireJS environment

I am attempting to convert a classic JavaScript "class" into an AMD module. However, I also need to keep exporting the class into the global namespace because some legacy code needs it. I've tried this, however, the global object is not created. What am I doing wrong?
define('VisitorManager', function () {
var VisitorManager = function () {
"use strict";
// ...
};
VisitorManager.prototype.hasExistingChat = function () {
// ...
};
//expose globally
this.VisitorManager = VisitorManager;
//return AMD module
return VisitorManager;
});
To expose your module globally, you need to register it in the global object.
In the browser the global object is window:
window.VisitorManager = VisitorManager;
In a Node.js environment the global object is called GLOBAL:
GLOBAL.VisitorManager = VisitorManager;
To use the class in both a legacy environment and with RequireJS, you can use this trick:
(function() {
var module = function() {
var VisitorManager = function() {
"use strict";
// ...
};
// Return the class as an AMD module.
return VisitorManager;
};
if (typeof define === "function" && typeof require === "function") {
// If we are in a RequireJS environment, register the module.
define('VisitorManager', module);
} else {
// Otherwise, register it globally.
// This registers the class itself:
window.VisitorManager = module();
// If you want to great a global *instance* of the class, use this:
// window.VisitorManager = new (module())();
}
})();

RequireJS: Dynamic dependencies within nested require of a module

Suppose there is a function that returns an array containing dynamic dependencies.
Then within a module B these dependencies are used. Another module A in turn uses module B.
A.js
define([B], function(moduleB){
moduleB.m();
})
B.js:
define([ dep1, dep2 ], function( dep1, dep2 ) {
var dyndeps = dep2.getDynDeps();
var moduleB = {}
require(dyndeps, function() {
moduleB.m = function() { ... };
})
return moduleB;
});
The problem with this approach is, that the inner require is executed asynchronously, so the method m is not available in time.
Since B.m is provided by a dynamic dependency, B should provide an interface for waiting on it to become available. There are a number of plugins to allow that, e.g. rq (using Q promises), promise (using jquery, Q, RSVP or ES6 promises), promiseme (self-contained?).
Using one of those, B would not return moduleB, but a promise. The nested require call would resolve the promise with complete moduleB. A would require <PLUGIN>!B. E.g. using promise and jquery:
// A.js
define(["promise!B"], function(moduleB){
// B is complete now
moduleB.m();
})
// B.js
define([ "dep1", "dep2", "jquery" ], function( dep1, dep2, $ ) {
var dyndeps = dep2.getDynDeps();
var moduleB = {};
var loaded = new $.Deferred();
require(dyndeps, function() {
moduleB.m = function() { ... };
loaded.resolve(moduleB);
})
return loaded.promise();
});
The only remaining problem with this approach is that the client code (A.js) needs to know to depend on B in a special way. A better solution would be to have B hide its dynamic nature like:
// A.js
define(["B"], function(moduleB){
moduleB.m();
})
// B.js
define([ "promise!dynamicB" ], function( moduleB ) {
return moduleB;
});
// still inside B.js define a "private" named module:
define("dynamicB", ["dep1", "dep2", "jquery"], function() {
var dyndeps = dep2.getDynDeps();
var loaded = new $.Deferred();
var moduleB = {};
require(dyndeps, function() {
moduleB.m = function() { ... };
loaded.resolve(moduleB);
})
return loaded.promise();
});
Now B can be used just as any other module.

Javascript Module pattern, Prototype and Google Closure

I am having trouble getting this code structure to survive obfuscation with the Google Closure Compiler. Here's some sample code:
var MyModule = (function()
{
function myModule()
{
// Constructor
}
function moduleFoo(url)
{
// Method
}
function moduleBar()
{
// Method
}
myModule.prototype = {
constructor: myModule,
foo: moduleFoo,
bar: moduleBar
};
return myModule;
})();
Elsewhere in my code I need be able to write things like the following:
var myMod = new MyModule();
myMod.foo();
myMod.bar();
However the compiler is renaming everything (as expected). How can I make the prototype that I have defined available elsewhere in my code after obfuscation? I have tried exporting as follows:
// In place of the prototype object above
myModule.prototype['constructor'] = myModule;
myModule.prototype['foo'] = moduleFoo;
myModule.prototype['bar'] = moduleBar;
window['myModule'] = myModule;
But things seem to break down either when the prototype methods are called or when their corresponding closures are executed.
Any help is appreciated.
This exact pattern does not work well with Closure-compiler using ADVANCED_OPTIMIZATIONS. Instead, you will need to slightly refactor your code:
/** #constructor */
function MyModule()
{
// Constructor
}
(function() {
function moduleFoo(url)
{
// Problem using "this" keyword. Will require #this annotation.
}
MyModule.prototype = {
foo: moduleFoo
};
MyModule.prototype.bar = function() {
// "this" keyword works fine.
};
})();
Or like:
/** #const */
var MyNamespace = {};
(function() {
/** #constructor */
MyNamespace.MyModule = function() {};
MyNamespace.MyModule.prototype = {
constructor: function() {},
foo: function(url) {},
bar: function() {}
};
})();
With either of the above methods your exports should work correctly.
Note: The second option will only work with a compiler built from the latest source as it involves a bug that was just fixed last week.

I want to return a function and have it evaluated in the calling module

I'm not sure why this isn't working. I wrapped my function in an anonymous function with the belief that it wouldn't get evaluated until it was called elsewhere. But I'm getting an error about "dropchart" not being defined, so it's getting evaluated within this module.
define(function() {
var stage = {
before: {
createCanvas: function() {
return function() {
before(function(done) {
this.canvas = new dropchart.Canvas({
canvas: argsFor.canvas(),
data: argsFor.data()
});
done();
});
};
}
}
};
return stage;
});
This is how it is called from the other module:
define(['jquery', 'dropchart', 'argsFor', 'stage'],
function($, dropchart, argsFor, stage) {
var should = chai.should();
var xAxisSpec = {
run: function() {
describe('xAxis', function() {
stage.before.createCanvas();
...
The stage module has the argsFor and dropchart dependencies, not the calling module.
The calling module cannot somehow make those available for stage module, unless you pass them as arguments in stage.before.createCanvas or you put them to global scope which defeats the whole point of require.js

Categories

Resources