Wednesday, June 13, 2012

The V in MVC

I just read http://addyosmani.com/blog/digesting-javascript-mvc-pattern-abuse-or-evolution/ and it's got some good points. The main points you should take away is that there are already some great frameworks to work with and that there are many different approaches to the classic pattern. Most of these approaches are similar enough that you won't feel out of place if you switch between frameworks.

There is one point that I disagree with however, and that is that a view must know about the existence of models. I agree this is how a lot of frameworks work, especially when you think about ones that offer binding through templates or attributes on dom elements - but it doesn't have to.

I wrote PlastronJS and it follows a similar model as most MV* frameworks out there where the view is really little more than a template. The reason for this is that it's easier to get started and usually when you're doing a single page application there is no need for a great deal of abstraction.

So we turn our attention to the V in MVC. How can we have a view without knowing about the model? Well the easiest way is to build a view before building anything else. The second is to give it an interface that we can get, set and listen to and that's it. Here is a rather simple example:

View = function(text) {
  this.element = $('<div>').text(text);
}

View.prototype.setText = function(text) {
  this.element.text(text);
  this.emit('textChanged');
}

View.prototype.getText = function() {
  return this.element.text();
}

We've created a view. It can be updated by passing in text and if anyone makes a change to the view then it will emit that the text has been changed and the text can be read. It doesn't know anything about a models existence - nor what type of model. We could hook up any type of model to this view. Knowing about the model is the job of the control. The control is what knows about the model and the view.

So what is the advantages of having a separate view like this and not including it in with the controller or binding it in the DOM? Well for one thing the view does not have to know about the model. You can make simple and generic views that do things like display a text area or a graph and then re-use those views. You can also easily swap out views for different devices. If you want to change the view you won't have to change the rest of the control and it's bindings (unless it implements a different interface). There is also the possibility of building up larger views by adding together smaller views (it's like point-free programming for UI!)

So why don't more frameworks (including my own) do this? Well it's just harder to get started with and takes more code. You have to defined these views to begin with. Most people will look at it and think it's an awful lot of work to do to get a single page app going, and it is. You won't see and advantage to building an app with an abstracted view until much later in development.

**EDIT**

here is a little more detail about how to use it. Let's take the text view from above - we can use this to display any model. Let's say we have the user as a model - the user might have many different things on it (like usage limits) but we want to display the username at the top of the page. All we need to do is create a Control that pipes in the username of the model. So something like:

UsernameControl = function(model, view) {
  this.model = model;
  this.view = view;
  this.view.setText(model.get('username');
  this.model.on('username:change', function(username) {
    view.setText(username);
  });

usernameC = new UsernameControl(user, new View());

Now we may also have a shopping cart model and want to to display the amount, we can do exactly the same but create a shoppingCartControl, pass in a new instance of the view and hook up the total to the text. The trick is that the view stays generic and it's the control that hooks up what should be displayed. This is a simple example with only one parameter being passed through but you could build up views from smaller views or even have a view factory (one that will replicate the "text" div however many times you need for different values).

3 comments:

  1. How would this work with a model that is anything more than a single chunk of text?

    Either the view needs to know about the model or you need to add something to perform this role that sits between the view and model...

    Maybe you could show a working example?

    ReplyDelete
    Replies
    1. I've put an edit in to the blog showing how to hook it up with a control. The trick is with the single chunk you can show any chunk you want - you could even have several controls/views to display them all or you could build a view out of smaller views (I might actually make something to do this if I have time and put it on github) or you could make views which show three pieces of text, or have two input boxes and a bit of text (You'll notice I put an emit on the view so that the control can listen to changes by the user and pass that to the relevant model).

      hope that helps

      Delete
  2. This is exactly the MVP pattern. The Control in your example is the Presenter.

    ReplyDelete