MVP  - Model View Presenter

A modification of MVC.

The Controller is named Presenter as it takes a more active role in updating the View. In MVP the Model and View do not communicate directly with each other; all communication goes via the Presenter.



Overview


MVP is a variation of the MVC pattern. In MVP the Controller has been renamed Presenter because it takes on a much greater role. The Presenter makes decisions how to respond to events it receives from the View, but, more importantly, it can update the View directly. Hence its name 'Presenter': it presents the data and changes the presentation.

In MVP the role of the View is limited to notifying the Presenter of events that happen on the page, such as button clicks or data entry. The View does not have any business logic which is now the responsibility of the Presenter.

The Presenter communicates with the Model by updating its data. The Model does not notify the View but instead may notify the Presenter of any model changes. Below is a diagram of MVP and its data flow. We see that the Presenter sits in between the View and the Model and all communication must flow through the Presenter. In MVP there is no direct communication between the View and the Model.

To highlight the differences between MVC and MVP we show them side by side in a diagram. The size of the component boxes is a measure of their responsibilities, that is, the larger the box, the larger its role and responsibility. Notice the differences in data flow: in MVC it is circular and in MVP it is up and down the components.

When developing complex applications, MVP tends to be a more natural pattern than MVC. Developers like MVP because the UI is usually not the best place to implement business logic; it is better to maintain that at a central location.

Furthermore, MVP forces you to think in terms of reusability which improves the modeling and coding process. For example, when dealing with many pages, each with their own validation logic, it can be very difficult to find candidates for reuse. MVP helps in structuring you app from the onset.

Another benefit of MVP is testability: compared to MVC, MVP is much easier to test. User Interfaces are notorious difficult to test and having a relatively small and more passive view makes it easy to replace it with a Mock object and perform unit- or system-testing.

Next, we'll examine how to code MVP in JavaScript.

Implementing MVP in JavaScript
We will use the same page as MVC. This will allow us to focus on the pattern itself. Here is the HTML markup:

<div>
    MVP<br />
    <select id="nameList" size="15">lt;/select>
    <div id="nameCount">Total users: 0</div>
    <button id="addButton">Add User</button>
    <button id="removeButton">Remove User</button>
</div>

Just like the MVC example, it has a list control with user names and two buttons; one to add a new user, and one to delete the currently selected user. Also included is a div element that displays the total number of users. As a reminder, here is what the page looks like:

Let's now move on to the code starting with the Model and the Observer helper object.

var Model = function (names) {
    this.names = names;

    this.nameAdded = new Observer();
    this.nameRemoved = new Observer();
};

Model.prototype = {

    add: function (name) {
        this.names.push(name);
        this.nameAdded.notify(this.names);
    },

    remove: function (index) {
        this.names.splice(index, 1);
        this.nameRemoved.notify(this.names);
    },

    getNames: function () {
        return this.names;
    }

};


var Observer = function () {
    this.observers = [];
};

Observer.prototype = {

    attach: function (callback) {
        this.observers.push(callback);
    },

    notify: function (n) {
        for (var i = 0, len = this.observers.length; i < len; i++) {
            this.observers[i](n);
        }
    }
};

These are exactly the same as in MVC.

Recall that in MVC the nameAdded and nameRemoved Observers were used in notifying the View of any data changes. In MVP you will see that the Presenter gets notified instead.

This works well in asynchronous scenarios in which the Model gets updated from a remote server or database and the Observers notify any event handlers in the Presenter of any data changes. The Presenter then makes the business decisions and updates the View accordingly.

Next we will examine the View which is listed below.

var View = function (elements) {

    this.elements = elements;
};

Yes, that is all that is left of the View. It is an array of DOM elements that the Presenter interacts with. You can already see that this View is far easier to mock in testing scenarios compared with the View we had in MVC.

Let's move on to Presenter where the bulk of the logic resides:

var Presenter = function (model, view) {
    this.model = model;
    this.view = view;
    this.index = -1;
};

Presenter.prototype = {

    init: function () {

        var self = this;

        this.view.elements.addButton.click(function () {
            self.addName();
        });

        this.view.elements.removeButton.click(function () {
            self.removeName();
        });

        this.view.elements.nameList.change(function (e) {
            self.index = e.target.selectedIndex;
        });

        this.model.nameAdded.attach(function (n) {
            self.refresh(n);
        });

        this.model.nameRemoved.attach(function (n) {
            self.refresh(n);
        });


        this.refresh(this.model.getNames());
    },

    addName: function () {
        var name = prompt('Add a new user name: ', '');
        if (name) {
            this.model.add(name);
            this.index = -1;
        }
    },

    removeName: function () {
        if (this.index > -1) {
            this.model.remove(this.index);
            this.index = -1;
        }
        else {
            alert("No name was selected");
        }
    },

    refresh: function (names) {

        this.view.elements.nameList.html('');

        for (var i = 0, len = names.length; i < len; i++)
            this.view.elements.nameList.append(
                 '<option>' + names[i] + '</option>');

        this.view.elements.nameCount.text("Total names: " + len);
    }
};

You will recognize most of the logic, much of it from the original View in MVC. The initialization has migrated here, but more interestingly the refresh method also resides here. The refresh method updates the controls on the page: the View is not involved at all.

Let's go back to the init method. In it, the Presenter registers for events that occur on both the View and one the Model. It listens to the View's click events and the list's selection change events as well as the Model's nameAdded and nameRemoved events. The Presenter is the central hub in MVP.

Finally, let's run the system and review the startup code:

var model = new Model(['Bob Smith', 'Cindy Jackson', 'Alan Wong']);

var elements = {
    nameList: $('#nameList'),
    nameCount: $("#nameCount"),
    addButton: $('#addButton'),
    removeButton: $('#removeButton')
};

var view = new View(elements);

var presenter = new Presenter(model, view);
presenter.init();

From a separation of concerns' perspective this startup code feels right. First, the Model is created and initialized with three user names. There are no external dependencies. Next, the View is created with just the visual elements that we are interested in. Again, there are no dependencies. Then the Presenter is created with references to both the model and view instances, exactly the way you would expect it to be. The final call to init ties all components together and the page is ready to go!

The MVP app below is ready to run:


MVP

Count

  

MVP is an elegant pattern: each part has a clear responsibility and they are loosely coupled which is another good thing; it makes our code clean, clear, and highly maintainable.

As an aside: when reading up on MVP you may discover there are really two flavors of this pattern: one is called Passive View and the other is Supervising Controller. Our model is the Passive View which is the more common approach. The Supervising Controller simply adds bidirectional data flow between the Model and the View, so it a bit closer to MVC.

Whatever the intricate details of these patterns are it turns out that the JavaScript community has taken a lot of creative freedom with these patterns. From the 25 or so popular open source MV Frameworks only a few are easy to categorize as MVC, MVP, or MVVM; the rest is something like MV and 'Anything'. For example, the popular Backbone framework follows MVC, but the C stands for Collections, not Controller. Backbone also has a Router, which looks like a Controller.

As a JavaScript developer the main thing to understand is the separation of concerns, the loose binding, and the role that each component plays within each MV pattern.

Next we'll review the last of the MV patterns: MVVM.




  MVC
MVVM