Reading the requireJs documentation,
in order to fix the Circular Dependencies, is suggested to use exports to create an empty object for the module that is available immediately for reference by other modules.
I try this code but it seems to do not work. What is wrong?
P.S.:
read the comments for seeing the output,
especially the B module inside setTimeout call.
// A module
define([
'b'
], function (b) {
console.log('B:', b); // B, Object
var A = {
boo: 1
};
return A;
});
// B module
define([
'a',
'exports'
], function (a, exports) {
console.log('A:', a); // A, undefined (as I was expecting)
exports.A = function () {
return a;
}
var B = {
bar: 1
};
setTimeout(function () {
console.log('exports.A', exports.A()); // exports.A undefined
// I would like to access the A object
// which is defined in A module
}, 500);
return B;
});
// main.js
(function () {
define([
'a'
], function () {
});
}());
You should be able to use the synchronous version of require() in your B module to access the "A" module:
// B module
define([
'a',
'exports'
], function (a, exports) {
console.log('A:', a); // A, undefined (as I was expecting)
exports.A = function () {
return require('a');
}
...
});
I often have circular issues using AMD modules to build an application core that both stands up many modules and contains config or other useful objects for those modules to use.
I did some experimenting today and this seems to work pretty well.
define(['exports', 'underscore', './config', './mediator'],
function (exports, _, Backbone, config, Mediator){
Core = /* ... */
// Publicize a core 'singleton' so that it's dependencies can access it, and so can modules that define it as a dependency themselves.
core = new Core()
exports.core = core //publicize it in a way that supports circularity
return core // And also publicize it normally
}
)
The objects are both '===' equal to each other, so this seems very promising.
EDIT:
The above method doesn't work when optimized. Here's another method that may (untested):
https://github.com/requirejs/example-multipage/blob/master/www/js/app/main1.js#L2
define(function (require) {
var $ = require('jquery'),
lib = require('./lib'),
Core;
Core = /* ... */
return new Core()
});
One option would be not to return the module itself, but a function that instantiates the module (in this example it would a constructor as defined in typescript, at the bottom is the generated js code -note that interfaces do not generate .js code)
File IA.ts
/// <reference path="IB.ts" />
interface IA{
funcA();
_classB : IB;
}
File IB.ts
/// <reference path="IA.ts" />
interface IB{
funcB();
_classA : IA;
}
File ClassA.ts
/// <reference path="IA.ts" />
/// <reference path="IB.ts" />
export class ClassA implements IA
{
_classB : IB = null;
constructor(classB : IB)
{
this._classB = classB;
if (classB){
this._classB._classA = this;
}
return this;
}
funcA(){
console.log('I am ClassA');
}
}
File ClassB.ts
/// <reference path="IA.ts" />
/// <reference path="IB.ts" />
export class ClassB implements IB
{
_classA : IA = null;
constructor(classA : IA)
{
this._classA = classA;
if (classA){
this._classA._classB = this;
}
return this;
}
funcB(){
console.log('I am ClassB');
}
}
File MainTest.ts
/// <reference path="../../def/require.d.ts" />
/// <reference path="IA.ts" />
/// <reference path="IB.ts" />
define(['ClassA', 'ClassB'],
function (classA, classB)
{
var aa : IA = new classA.ClassA();
var bb : IB = new classB.ClassB(aa);
bb.funcB();
aa._classB.funcB();
bb._classA.funcA();
aa.funcA();
});
And the generated js code:
File ClassA.js
define(["require", "exports"], function(require, exports) {
var ClassA = (function () {
function ClassA(classB) {
this._classB = null;
this._classB = classB;
if (classB) {
this._classB._classA = this;
}
return this;
}
ClassA.prototype.funcA = function () {
console.log('I am ClassA');
};
return ClassA;
})();
exports.ClassA = ClassA;
});
File ClassB.js
define(["require", "exports"], function(require, exports) {
var ClassB = (function () {
function ClassB(classA) {
this._classA = null;
this._classA = classA;
if (classA) {
this._classA._classB = this;
}
return this;
}
ClassB.prototype.funcB = function () {
console.log('I am ClassB');
};
return ClassB;
})();
exports.ClassB = ClassB;
});
File MainTest.js
define(['ClassA', 'ClassB'], function (classA, classB) {
var aa = new classA.ClassA();
var bb = new classB.ClassB(aa);
bb.funcB();
aa._classB.funcB();
bb._classA.funcA();
aa.funcA();
});
finally, the output will be:
I am ClassB
I am ClassB
I am ClassA
I am ClassA
Related
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.
Suppose I have a TypeScript module defined like:
module Foo {
export var now = new Date();
}
This gets transpiled to:
var Foo;
(function (Foo) {
Foo.now = new Date();
})(Foo || (Foo = {}));
If I were writing in pure JavaScript, I would have included something like:
var Foo;
(function (Foo) {
if (window.Foo) return; // <-- PREVENT DUPLICATES!
Foo.now = new Date();
})(Foo || (Foo = {}));
to ensure that this module gets defined only once even if the <script/> gets included multiple times in a page. How do I achieve the same effect in TypeScript?
If you are writing your whole program in TypeScript, the compiler checks this for you...
module Foo {
export class X {
go() {
alert('First');
}
}
}
module Foo {
export class X { // Duplicate identifier X
go() {
alert('Second');
}
}
}
If you can, using external modules along with a module loader (RequireJS for example) ensures that non of your code needs to be added to the global scope, so it wouldn't conflict...
import foo = require('./foo');
export class Y {
go() {
return new foo.X();
}
}
Compiles to:
define(["require", "exports", './foo'], function(require, exports, foo) {
var Y = (function () {
function Y() {
}
Y.prototype.go = function () {
return new foo.X();
};
return Y;
})();
exports.Y = Y;
});
From this discussion on TS forums, I was able to come up with the following solution:
module window {
if (window.Foo) return;
export module Foo {
export var now = new Date()
}
}
This compiles to:
var window;
(function (window) {
if (window.Foo)
return;
(function (Foo) {
Foo.now = new Date();
})(window.Foo || (window.Foo = {}));
var Foo = window.Foo;
})(window || (window = {}));
Lets say I have a controller in AngularJS:
myApp.controller('SearchController',
function ($scope, UserService) {
// for intellisense, UserService is undefined here
var user = UserService.getUsers().then(function(data){
// yada yada
}, function(err){
// yada yada
});
});
However, in my intellisense file, I can dynamically inject UserService to get its functions like this:
intellisense.addEventListener('statementcompletion', function (event) {
// tried doing this, but doesn't work!
// event.target = {};
var injector = angular.injector(['ng', 'myApp']);
var dependency = injector.get(event.targetName);
event.items = [];
for (method in dependency) {
intellisense.logMessage(method);
event.items.push({ name: method, kind: 'field', value: function () { } });
}
});
Now, if I have a global variable (or function variable) defined as UserService = {} and inside my controller function I type UserService. I will get a pop up of all the functions in the service. But if I don't have it defined, since it is interpreted as undefined by intellisense, it can't show me the options even though statementcompletion is working (as seen in the Javascript Language Service console).
My question is, apart from annotating the function, is there anyway to define UserService as an object in the intellisense file? Defining event.target = {} does not work (see intellisense code above).
One way that works is to "call" the component functions (controller, services, etc) from intellisense code with empty objects.
I am sure this can be a lot cleaner but here's what I've done:
https://github.com/diwasbhattarai/angularjs-intellisense
By John Bledsoe: https://github.com/jmbledsoe/angularjs-visualstudio-intellisense/
references.js - add this file as reference in Tools>Options>TextEditor>Javascript>Intellisense>References
/// <reference path="../source/lib/assetBundle.js" />
/// <reference path="_moduleDecorator.js" />
/// <reference path="_componentDecorator.js" />
/// <reference path="../source/app/appBundle.js" />
intellisense.addEventListener('statementcompletion', function (event) {
// for core angular objects
addComponentToIntellisense('ng');
// for custom objects in application modules
for (var moduleIndex in modules) {
addComponentToIntellisense(modules[moduleIndex]);
}
function addComponentToIntellisense(module) {
var $injector = angular.injector(['ng', module]),
dependency = $injector.get(event.targetName),
dep;
if (typeof dependency === "function") dep = new dependency();
else dep = dependency;
for (var method in dep) {
event.items.push({ name: method, kind: 'field', value: dependency[method] });
}
}
});
_moduleDecorator.js - to keep track of all the modules in your app
//_moduleDecorator
(function () {
var originalModule = angular.module;
// TODO change to array
modules = {};
var rep = false;
var count = 0;
angular.module = function () {
for (var k in modules) {
if (modules[k] === arguments[0]) {
rep = true;
break;
}
}
if (!rep) modules[count++] = arguments[0];
return originalModule.apply(angular, arguments);
};
})();
_componentDecorator.js - to "call" component functions with empty object parameter
(function () {
// pick all the components in all modules and initialize them for intellisense
for (var moduleIndex in modules) {
var currentModule = angular.module(modules[moduleIndex]),
queue = currentModule._invokeQueue,
// add other components such as value, provider, etc later
angularComponents = ['controller', 'factory', 'service', 'value'];
for (var i = 0; i < angularComponents.length; i++) {
var currentComponent = angularComponents[i],
originalComponentFn = currentModule[currentComponent];
currentModule[currentComponent] = (function (currentModule, queue, originalComponentFn) {
return function () {
originalComponentFn.apply(currentModule, arguments);
initializeComponents(queue);
};
})(currentModule, queue, originalComponentFn);
}
}
function initializeComponents(queue) {
var elem = queue.filter(function (element) {
var componentName = element[2][0].toLowerCase();
return (componentName.indexOf(componentName) !== -1);
});
for (var i = 0; i < elem.length; i++) {
var tempComp = elem[i][2][1],
compFunc;
// for array notation for DI
if (typeof tempComp !== "function") {
compFunc = tempComp[tempComp.length - 1];
} else {
compFunc = tempComp;
}
// 10 parameter dependencies initialization for now
compFunc({}, {}, {}, {}, {}, {}, {}, {}, {}, {});
}
}
})();
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
I've got a module that looks like this:
var MyModule = module.exports = function MyModule(opts) {
opts = (opts === Object(opts)) ? opts : {};
if (!(this instanceof MyModule)) {
return new MyModule(opts);
}
for (var key in opts) if ({}.hasOwnProperty.call(opts, key)) {
this.config[key] == opts[key];
}
};
MyModule.prototype.config = {
something:'value'
}
MyModule.prototype.put = function put(info, cb) {
//do stuff
};
However, when I use it like this:
var myModule = require('myModule.js');
myModule.put({test}, function(){
//some callback stuff
});
I get the following error:
TypeError: Object function MyModule(opts) {
opts = (opts === Object(opts)) ? opts : {};
if (!(this instanceof MyModule)) {
return new MyModule(opts);
}
for (var key in opts) if ({}.hasOwnProperty.call(opts, key)) {
this.config[key] == opts[key];
} } has no method 'put'
It appears I have something wrong with my MyModule.prototype.put ?
You wrote :
var myModule = require('myModule.js');
myModule.put({}, function(){
//some callback stuff
});
Here myModule is in fact MyModule, a constructor function. So what you are doing is MyModule.put(), a call to a "static" method of MyModule. MyModule.prototype.put defines an "instance" method so you have to instanciate first :
var MyModule = require('./myModule.js');
var myModule = new MyModule();
// or as you used `if (!(this instanceof MyModule)) { … }`
var myModule = MyModule();
myModule.put({}, function () {});
So basically your code needs just a pair of () to work :
MyModule().put({}, function () {});
// same as
(new MyModule).put({}, function () {});
Récap :
var MyModule = function () {
// Construct object
};
MyModule.staticMethod = function () {
this; // is bound to `MyModule` function object
};
MyModule.prototype.instanceMethod = function () {
this; // is bound to the `MyModule` instance object it’s called from
};
// Usage
MyModule.staticMethod();
var instance = new MyModule();
instance.instanceMethod();
With this code var myModule = require('myModule.js'); your myModule variable looks like a constructor function, not an instance of myModule.
Try instantiating your module first:
var MyModule = require('myModule.js');
var myModule = new MyModule(); // Create an instance of your module.
// Now use it.
myModule.put(/*... your code here ...*/);