Maria: An MVC framework for JS
github.com
github.com
For almost a year I have been writing web apps with no controllers, and it feels right. So far so good. It simplifies routing and a couple other things.
That's not to say you can take the avg MVC framework and drop the controller. A couple new patterns were really important to keep things sane. For example, there is still technically a default controller that does the default request handling for views. Happy to share more if anyone is curious.
What are patterns you mentioned?
So now, the models only expose a REST API beyond the "class wall" (so to speak), which feels rather natural when called from views in template syntax. Client-side frameworks do it this way already, but I'm talking server side.
A great side effect of models speaking REST natively is, it becomes trivial to create a public API, just add authentication.
There's a few other things, like automatic request view routing.
I'm curious what role the controller objects play here.
However, the way the methods are connected appears to be the uiActions attribute of the view object, which seems unnecessarily tight coupling between the controller and view. Also, the example code makes the view code responsible for querying the form, when it seems this should be the responsibility of the controller.
For some reason I still have a gut feeling that the view/controller split can allow generic views with custom controllers or generic controllers with custom views, but in the real world it seems the view/controller separation is still extremely fuzzy.
Morphic, the ui framework used by squeak, does away with the controller concept, leaving input handling up to the Morph (view) which is an interesting data point.
Thanks for taking a look at Maria. You definitely got the gist of how things work with Maria but there are a few subtleties that might change your perspective on the relationship between views and controllers in an app built with Maria.
If a view has the following
maria.ElementView.subclass(myApp, 'MyView', {
uiActions: {
'click .alpha' : 'onClickAlpha' ,
'mouseover .beta': 'onMouseoverBeta'
},
...
That auto-generates two methods on the view that forward handling to the controller. myApp.MyView.prototype.onClickAlpha = function(evt) {
this.getController().onClickAlpha(evt);
};
myApp.MyView.prototype.onMouseoverBeta = function(evt) {
this.getController().onMouseoverBeta(evt);
};
So it is actually the view that handles the DOM events.If you don't want the controller to handle the raw DOM event or you don't want a controller involved in handling the event at all, you can define the handler methods on the view yourself and then the auto-generated methods will not be created. For example,
maria.ElementView.subclass(myApp, 'MyView', {
uiActions: {
'click .alpha' : 'onClickAlpha' ,
'mouseover .beta': 'onMouseoverBeta'
},
properties: {
onClickAlpha: function(evt) {
// not sending evt to the controller
// and controller method name is different
this.getController().onSomething(1, 2, 3);
},
onMouseoverBeta: function(evt) {
alert('no controller involved here');
},
...
Also about who should query the form data: the view or the controller. I believe the view should be the only player in the game that knows about the DOM. If the way that the form data has to be retrieved from the DOM changes, then only the view needs to be updated. This makes sense to me as the view is the one that creates the form so the view should encapsulated all access to the form.In the real world, the view/controller separation can be fuzzy. With Maria you can easily live without any controllers and have the views handle all user events or you can go wild with controllers and have all kinds of interchangeable view behavior thanks to the flexibility provided by controllers and the strategy pattern.
I'm still interested in hearing a convincing argument for decoupled controllers. Your statement that the view should be responsible for DOM interrogation rings true to me, and seems to reinforce the idea that the view should just handle input instead of splitting it out into a controller.
I am finding, while programming example applications for Maria, that splitting the event handling out to the controller is forcing me to make better APIs on my views to keep the DOM encapsulated. Allowing the controller to get the form data, without exposing any DOM information, for example. The view code ends up being very satisfying in a way I haven't experienced before.
What I'm hoping will happen is that after using strict view and controller separation for a while, I'll start to get insights where the strategy part could be used more. I think its the kind of thing that will sneak up on me over time and I'll start to see more uses for it.
For example, @fzzy mentioned the controller in smalltalk being used to poll the mouse & keyboard. In the browser, you don't have the same issues-- you can listen for events to be fired. Also, for example, in the browser, you have the DOM, which is communicated with and manipulated differently than UI widgets on the desktop.
You'll find that all the MV* patterns (like MVC (in all its variations), MVP, MVVP, etc) have the concept of the domain model, and some object that's tightly coupled to the UI/DOM, and usually some middle layer. As long as you're using one of these types of patterns you'll least be able to take advantage the benefits that this family of patterns provides.
I use controllers to implement the strategy to be applied to user events, and this keeps my code focused (SRP/DRY).
I think removing the controller is a bad choice as it would require the view to know too much, and besides having the controller in the mix is made easy by Maria. Any time I have diverged from using the Controller the code started to deteriorate with views having to handle increasing responsibility.
Yes - MVC is part of a larger set of patterns and the thing I like most about Maria is that it focuses on being just MVC. Now I'm free to look for implementations of other patterns as I need them. Hopefully from Peter as well, as he really understands a pattern before he implements it.
- James.