Model-View-Controller in JavaScript
tl;dr: How does one implement MVC in JavaScript in a clean way?
I'm trying to implement MVC in JavaScript. I have googled and reorganized with my code countless times but have not found a suitable solution. (The code just doesn't "feel right".)
Here's how I'm going about it right now. It's incredibly complicated and is a pain to work with (but still better than the pile of code I had before). It has ugly workarounds that sort of defeat the purpose of MVC.
And behold, the mess, if you're really brave:
// Create a "main model"
var main = Model0();
function Model0() {
// Create an associated view and store its methods in "view"
var view = View0();
// Create a submodel and pass it a function
// that will "subviewify" the submodel's view
var model1 = Model1(function (subview) {
view.subviewify(subview);
});
// Return model methods that can be used by
// the controller (the onchange handlers)
return {
'updateModel1': function (newValue) {
model1.update(newValue);
}
};
}
function Model1(makeSubView) {
var info = '';
// Make an associated view and attach the view
// to the parent view using the passed function
var view = View1();
makeSubView(view.__view); // Dirty dirty
// Return model methods that can be used by
// the parent model (and so the controller)
return {
'update': function (newValue) {
info = newValue;
// Notify the view of the new information
view.events.value(info);
}
};
}
function View0() {
var thing = document.getElementById('theDiv');
var input = document.getElementById('theInput');
// This is the "controller", bear with me
input.onchange = function () {
// Ugly, uses a global to contact the model
main.updateModel1(this.value);
};
return {
'events': {},
// Adds a subview to this view.
'subviewify': funct开发者_Python百科ion (subview) {
thing.appendChild(subview);
}
};
}
// This is a subview.
function View1() {
var element = document.createElement('div');
return {
'events': {
// When the value changes this is
// called so the view can be updated
'value': function (newValue) {
element.innerHTML = newValue;
}
},
// ..Expose the DOM representation of the subview
// so it can be attached to a parent view
'__view': element
};
}
How does one implement MVC in JavaScript in a cleaner way? How can I improve this system? Or is this the completely wrong way to go, should I follow another pattern?
There are at least a couple of established and usable MVC frameworks for JavaScript JavaScriptMVC and pureMVC. There are probably more. I've used JavaScriptMVC for browser based and Air apps and keep coming back to it - it has its problems but I've found it to be quite useful.
There are other solutions too, have a look at Sammy, a new thing I've heard good things about. I haven't used myself but intend to try soon. I don't know enough about it to describe it properly, but to me it seems like a front controller which works on routes, a templating system and ReSTful data stores. I'm not sure if it is MVC but has similar ingredients.
I have to disagree with mway's answer. MVC may be a bit diferent to implement in JavaScript but its benefits are very important to organising this mess. The design patterns usually associated with OO languages don't go out the window just because js isn't class based.
I would say that MVC is more suitable for JavaScript apps than for request based (server side) applications. Those objects can hang around for a while in a one page JavaScript app - minutes if not hours - and having a well organised way of organising their interaction will make your code much more robust and easy to deal with. There are books on the subject.
A couple of other points regarding the code you posted.
- The view objects have responsibility for applying event listeners to DOM elements. This is the controller's job. The view just renders the HTML - the controller listens for the events and acts accordingly.
- Your models seem to know your views. The model layer should have minimal knowledge of the view layer (perhaps being registered as observers). Keep your model clean and to the point, I mean the business point - business logic. In js apps you may just be proxying for a sever side model layer but it is important for your sanity to keep your model to the business logic and nothing else. Application logic is the controllers job
To be honest, MVC isn't well-suited for Javascript. It can support the basic fundamentals of the design, sure - you can create pseudoclasses to act as controllers or models, support basic inheritance, and you can have it manipulate or create any number of DOM elements, but there's a price that you pay for that - in overhead, accessibility, and usability.
In my opinion, I consider Javascript more of an augmentation - the KISS mentality exists for a good reason. If you're interested in better ways to organize your code, there's always the option of packaging related functionality into modules (sic) and abstracting out portions as appropriate. For example, creating a factory to do more complex AJAX request management, or a pseudoclass to handle processing of similar types of data. Using a standard base function for controllers, another for models, etc, as prototypes for new instances of those objects can accomplish similar functionality... but again, it's sort of going against the grain of Javascript.
However, if you're stuck on the MVC idea just for the sake of structure, consider something like the following:
;(function(window, $) {
/**
* Event Object
* A quick description goes here.
**/
var Events = window.Events = {
'bindTrackables': function() {
$('a.trackable').live('click', function() {
if(!_gaq)
_gaq = [];
_gaq.push(['_trackPageview', '/ajax/foobar']);
});
},
'bindSomeEvent': function() {
// etc
}
};
/**
* Data Cache
* I'll need to remember stuff later, so I store it here
**/
var Cache = window.Cache = {
'data': {},
'store': function(key, value) {
Cache.data[key] = value;
},
'fetch': function(key) {
return Cache.data[key];
}
};
/**
* Request Object
* Stores native AJAX requests for later use
**/
var Request = window.Request = {
'current_requests': [],
'send': function(url, type, data, callback) {
Request.current_requests.push($.ajax({
'url': url,
'type': type,
'data': data,
'callback': callback
}));
},
}
// add some private logic here
})(window, jQuery);
It's extremely basic, but you get the idea. Modular code is key... in JS, this is more important than forcing your application (or the language) to fit a certain style.
精彩评论