MVP Pattern

This lesson discusses the MVP pattern in detail using a coding example.

What is the MVP pattern?

The MVP pattern stands for Model-View-Presenter. It is derived from the MVC pattern, which focuses on the user interface. MVP is focused on improving the presentation logic.

It consists of three components:

  • Model: provides the data that the application requires, and we want to display in the view.

  • View: to display the data from the model, it passes the user actions/commands to the presenter to act upon that data.

  • Presenter: acts as the middle man between the model and the view. Retrieves data from the model manipulates it, and returns it to view for display. It also reacts to the user’s interaction with the view.

What’s the difference between MVP and MVC?

In the MVC pattern, the controller acts as the mediator between the model and the view, and it can share multiple views. The view can also communicate directly with the model by observing it for changes and updating itself accordingly.

In the MVP pattern, the presenter acts as the mediator between the model and the view. It reacts to user actions; retrieves and manipulates model data, and returns it to the view for display. Hence, the model and the view are more separate in this pattern. The presenter also has a one to one mapping with the view. If the view is complex, it can use multiple presenters.

Example #

class Model{
constructor(text){
this.text = text;
}
setText(text){
this.text = text;
}
getText(){
return this.text;
}
}
class View{
constructor(){
this.presenter = null;
}
registerWith(presenter){
this.presenter = presenter;
}
displayError(){
console.log("Text is not in upper case");
}
displayMessage(text){
console.log("The text is: " + text);
}
changeText(text){
this.presenter.changeText(text);
}
}
class Presenter{
constructor(view){
this.view = view;
this.model = null;
}
setModel(model){
this.model= model;
}
getView(){
return this.view;
}
changeText(text){
if(text !== text.toUpperCase()){
this.view.displayError();
}else{
this.model.setText(text);
this.view.displayMessage(this.model.getText());
}
}
}
var model = new Model("Hello world!")
var view = new View()
var presenter = new Presenter(view)
presenter.setModel(model)
view.registerWith(presenter)
presenter.getView().changeText("unagi")
presenter.getView().changeText("UNAGI")

Explanation

The example above implements the MVP pattern to display some text. It has three components, the Model, View, and Presenter. Let’s discuss them one-by-one.

Model

class Model{
    constructor(text){
        this.text = text;
    }
    setText(text){
        this.text = text;
    }
    getText(){
        return this.text;
    }
}

The Model contains the data which includes the text to be displayed. It also has two functions: setText and getText to set and retrieve the text property.

View

class View{
    constructor(){
        this.presenter = null;
    }

    registerWith(presenter){
        this.presenter = presenter;
    }

    displayError(){
        console.log("Text is not in upper case");
    }

    displayMessage(text){
        console.log("The text is: " + text);
    }

    changeText(text){
        this.presenter.changeText(text);
    }
}

As discussed, the View is responsible for passing any user actions to the presenter. An example is that of the changeText function, which allows the user to change the text. As you can see, it notifies the presenter, which then calls the changeText function of its own. We will get into that when we discuss the Presenter. Similarly, the View also displays the updated data returned to it by the presenter. The dispayError and displayMessage functions are defined for that purpose.

Presenter

class Presenter{
    constructor(view){
        this.view = view;
        this.model = null;
    }

    setModel(model){
        this.model= model;
    }

    getView(){
        return this.view;
    }

    changeText(text){
        if(text !== text.toUpperCase()){
            this.view.displayError();
        }else{
            this.model.setText(text); 
            this.view.displayMessage(this.model.getText());
        }
    }
}

As discussed, the Presenter is the channel of communication between the model and the view. Hence, it initializes the view in its constructor and uses the setModel function to initialize the model.

The presenter exposes the getView function to return the view set in the constructor. Now, if a user tries to change the text in the view mode, the view relays this action to the presenter, which then executes its changeText function.

changeText checks whether the text that the user wants to set is in the upper case or not. If yes, it updates the model and sets the new text. Since the model gets updated, the function returns the updated data to the view for display:

this.model.setText(text);
this.view.displayMessage(this.model.getText()) 

However, if the text is not in the upper case, it calls the view to display the error.

if(text !== text.toUpperCase()){
    this.view.displayError();

When to use the MVP pattern? #

You can use this pattern:

  • If your application requires a lot of reuse of the presentation logic.

  • If your application requires a lot of user interaction.

  • If your application has complex views.

  • For easier testing as the presenter can provide a mock interface that can be unit tested.


Now that you know what the MVP pattern is, it’s time to implement it!