I\'m currently facing a conundrum: What is the right way to wire together 2 javascript objects?
Imagine an application like a text editor with several different fi
I would say, just wire them together:
function wireTogether() {
var v = new View();
var c = new Controller();
c.setView(v);
}
But then of course another question raises - how do you test the wireTogether() function?
Luckily, JavaScript is a really dynamic language, so you can just assign new values to View and Controller:
var ok = false;
View.prototype.isOurMock = true;
Controller.prototype.setView = function(v) {
ok = v.isOurMock;
}
wireTogether();
alert( ok ? "Test passed" : "Test failed" );
There is also a framework for dependency injection for JavaScript: https://github.com/briancavalier/wire
I'll try to take a stab at this, but it will be a little difficult without seeing any actual code. Personally, I've never seen anybody do such a specific attempt at (M)VC with JavaScript or IoC for that matter.
First of all, what are you going to test with? If you haven't already, check out the YUI Test video which has some good info on unit testing with javascript.
Secondly, when you say "the best way to wire up that aggregation" I would probably just do it as a setter w/the controller
// Production
var cont = new NotebookController();
cont.setView( new NotebookView() );
// Testing the View
var cont = new NotebookController();
cont.setView( new MockNotebookView() );
// Testing the Controller
var cont = new MockNotebookController();
cont.setView( new NotebookView() );
// Testing both
var cont = new MockNotebookController();
cont.setView( new MockNotebookView() );
But this makes some big assumption on how you've designed your controller and view objects already.
Dependency injection is probably your best bet. Compared to Java, some aspects of this are easier to do in JS code, since you can pass an object full of callbacks into your NotebookController. Other aspects are harder, because you don't have the static code analysis to formalize the interface between them.
Thanks for the insight. I ended up writing a simple JavaScript dependency injection utility. After debating for a while and your comments, it occured to me that DI was really the right answer because:
So here's the DI utility:
var Dependency = function(_name, _instance, _dependencyMap) {
this.name = _name;
this.instance = _instance;
this.dependencyMap = _dependencyMap;
}
Dependency.prototype.toString = function() {
return this.name;
}
CONCORD.dependencyinjection = {};
CONCORD.dependencyinjection.Context = function() {
this.registry = {};
}
CONCORD.dependencyinjection.Context.prototype = {
register : function(name, instance, dependencyMap) {
this.registry[name] = new Dependency(name, instance, dependencyMap);
},
get : function(name) {
var dependency = this.registry[name];
return dependency != null ? dependency.instance : null;
},
init : function() {
YAHOO.log("Initializing Dependency Injection","info","CONCORD.dependencyinjection.Context");
var registryKey;
var dependencyKey;
var dependency;
var afterDependenciesSet = [];
for (registryKey in this.registry) {
dependency = this.registry[registryKey];
YAHOO.log("Initializing " + dependency.name,"debug","CONCORD.dependencyinjection.Context");
for(dependencyKey in dependency.dependencyMap) {
var name = dependency.dependencyMap[dependencyKey];
var instance = this.get(name);
if(instance == null) {
throw "Unsatisfied Dependency: "+dependency+"."+dependencyKey+" could not find instance for "+name;
}
dependency.instance[dependencyKey] = instance;
}
if(typeof dependency.instance['afterDependenciesSet'] != 'undefined') {
afterDependenciesSet.push(dependency);
}
}
var i;
for(i = 0; i < afterDependenciesSet.length; i++) {
afterDependenciesSet[i].instance.afterDependenciesSet();
}
}
}
I have a inversion of control library for javascript, I'm pretty happy with it. https://github.com/fschwiet/jsfioc. It also supports events, so if you want to have a startup event thats fine. It could use more documentation...
http://github.com/fschwiet/jsfioc
Another (newer?) option, which has better documentation and support, is requireJS (http://requirejs.org/).