An MVP guide to JavaScript – Model-View-Presenter

In a previous post I described the benefits of MVP architecture (you can see this post). Now I will try to explain how to implement that design pattern, inspired from Google’s GWT, with JavaScript.

I would assume, for this demo, that you are familiar with the basics of jQuery.

What is MVP?

Model-View-Presenter is a design pattern which separates the code for a specific widget/functionality to three sections:

Model

In which the data model for the widget is defined.

View

In which the logic behind the UI is handled, with UI events, data visualization and other UI centric logic.

Presenter

Where the logic behind the functionality of the widgets rests, such as data manipulation, data storing and loading, application events, etc…

So how does it work?

When working with MVP, the presenter is your main guy, this object carries the logic behind your widget and thus comprises the bulk of your code.

Basic MVP pattern

The presenter requires the corresponding view, and a model. When the presenter gets the model, it updates the view with different handlers, and the view will then update the UI.

The presenter will register handlers in the view for any UI events that require some logic.

A major benefit with MVP is that you can create several views, which may look and behave differently, but share the same presenter. Meaning that they share the same functionality, but the UI looks and behaves differently.

Show me how it’s made!

First, for this demo, I will define the requirements, the application:

Sample MVP application

A small task list application.

How can this be implemented in Model-View-Presenter?

I will need to split the defined app into two widgets; a Single Task Widget and a Task List Widget.

Creating a Task Widget

The Model

I’ll start by defining a simple model for the task object:

function TaskModel(_text){
	var ID = (new Date()).getTime()
	this.getID = function(){
	return ID;
	}
    var Text = _text;
    this.getText = function(){
        return Text;
    }
    this.setText = function(value){
        Text = value;
    }
}

My preference is to make a getter and setter for each property in the model, it allows me to make changes to values when getting or setting them in a central location.

The Presenter

function TaskPresenter(_view){

	var view;
	var model;

	function init(){
		view = _view;
		view.addCheckedHandler(function(){
			view.remove();
		});
	}

	var public = {
		getView: function(){
			return view;
		},
		setModel: function(_model){
			model = _model;
			view.setModel(model);
		}
	}

	init();
	return public;
}

First thing you will notice is that the presenter object gets its view (the UI) object in the constructor.
In a private function I can setup the object’s logic, and the public functions define what the presenter is actually allowed to do.
As you can see, the getView() public function returns the view object which was supplied in the constructor, every presenter must expose this function.

Inside the init() function I registered a handler with the view that will fire when a task has been checked, this handler will tell the view to remove itself from the UI.

As you can see the presenter does not care how ‘checking the task’ was achieved, but it knows what to do once that action happened.

The View

function TaskView(){

	var html;

	function init(){
		html = $("<input type='checkbox'/><label></label></li>");
	}

	var public = {
		getHtml: function(){
			return html;
		},
		setModel: function(model){
			html.find("input").attr("id", model.getID());
			html.find("label").attr("for", model.getID());
			html.find("label").html(model.getText());
		},
		addCheckedHandler: function(handler){
			html.find("input").click(handler);
		},
		remove: function(){
			html.remove();
		}
	}

	init();
	return public;
}

The view contains an html property which is the HTML representation of this widget, a template.
All views must returns a public getHtml() function which will return the HTML object, the rendered template.


The public functions are those that I used in the presenter setup function, those functions represent the functionality of the view.

So I created a model, a presenter and a view for a single Task widget.
To use it, I need to do the following:

var model 	= new TaskModel("Hello world!");
var task 	= new TaskPresenter(new TaskView());

task.setModel(model);

$("body").append(task.getView().getHtml());

And this is the result:

Creating a list widget for the tasks

function ListPresenter(_view){

	var view;
	var model;

	function init(){
		view = _view;
		
		view.addCreateTaskHandler(function(taskTitle){
			var model 	= new TaskModel(taskTitle);
			var task 	= new TaskPresenter(new TaskView());
			task.setModel(model);
			
			view.addTask(task.getView());
		});
		
	}

	var public = {
		getView: function(){
			return view;
		}
	}

	init();
	return public;
}

The list presenter object registers a handler in the view when a new task is created, which then it will create a new task object and pass it’s view to the list view.
A presenter always handles with other presenter and a view always handles with other views.

function ListView(){

	var html;

	function init(){
		html = $("<div>"+
				"<h1>Awesome MVP task list</h1>"+
					"<fieldset><legend>Don't forget!</legend>"+
						"<ul id='tasklist'></ul>"+
					"</fieldset>"+
				"<h2>Add a new task:</h2>"+
				"What do you need to do? <input id='taskinput' placeholder='I need to do…'/> <input id='submittask' type='submit' value='Add'/>"+
				"</div>");
	}

	var public = {
		getHtml: function(){
			return html;
		},
		addCreateTaskHandler: function(handler){
			html.find("#submittask").click(function(){
				var newTaskTitle = html.find("#taskinput").val();
				html.find("#taskinput").val("");
				handler(newTaskTitle);
			});
		},
		addTask: function(taskView){
			html.find("#tasklist").append(taskView.getHtml());
		}
	}

	init();
	return public;
}

Since this is the main view for the application I included all the rest of the UI here, this isn’t necessary, it’s just for this demo’s purpose.
The view’s functionality is to create a task, and add the created task to the UI.

Now what’s left is simply instantiate this object and add it to the HTML:

var list = new ListPresenter(new ListView());
$("body").append(list.getView().getHtml());

And the result is:

Download the example here:
MVP task list example

13 comments
  1. tv case said:

    Pretty great post. I simply stumbled upon your weblog and wanted to say that I have really loved surfing around your blog posts. In any case I will be subscribing on your feed and I hope you write once more soon!

  2. here said:

    I was wondering if you ever considered modifying the design of your blog? It is very well written; I love what you have got to say. But maybe you can create a a bit more in the way of content so people might connect with it better. Youve got a great deal of wording for only having one or two graphics. Maybe you can space it out better?

  3. Thanks for your reply, I really like the simple design but you are correct with the readability of the font.
    Perhaps a larger serif font would be better. I will look for better designs in the coming days. Thanks again!

  4. Alex said:

    Great post! I am wondering if you could describe the diff to MVC if possible?

    • Thanks Alex!

      MVP is very similar and actually is a type of MVC, so the difference is only in the raw implementation in your code.

      The basic rule of MVC is that the logic, UI and data are separated. The question of how you separate them is answered differently in each framework (Angular, Backbone, Ember, etc…)

  5. Peter said:

    Hi !

    I don’t know why, but your demo don’t work.
    I just copy/paste your code and use your zipbut both case don’t work.

  6. Hi,

    Thank you for the post. There are a few things I don’t like:
    1. You are not using the getter and setter support that exists in JavaScript itself.
    set, and get.
    2. You are passing the model object to the view. This is against the intent of MVP.

    • Thanks!

      1. This post was written in 2012, and I don’t think that the native getters and setters were supported widely then, but I could be mistaken.
      2. You are correct, but I did it as a convenience so I wouldn’t have to create an update function for every UI element in the view. I think that as long as you won’t modify the model from within the view, it should be fine.

  7. Greg said:

    Great post, Roy!
    What is the TDD approach for you in order to get this MPV structure?

    • hanks Greg!

      Ok, so let’s think what we need for TDD.
      I want to create a test for my list, I guess that I would start with describing the functionality of the ctrl:


      // Make sure that when the view submitted a new item, add it to the list and verify it's length

      var MockView = function(){
      var handler;
      return {
      addCreateTaskHandler: function(_handler){
      handler = _handler;
      },
      mockCreate: function(text){
      handler(text);
      }
      }
      }

      var TaskModel = MockModel()...
      var TaskPresenter = MockTaskPresenter()...

      var view = new MockView();
      var ctrl = new ListPresenter(view);
      assert(ctrl.getModel().list.length).eq(0);

      view.mockCreate("test");
      assert(ctrl.getModel().list.length).eq(1);

      I would need to create a method that returns the model from the presenter evetually.

Leave a Reply

Your email address will not be published. Required fields are marked *