Sorting through long and cluttered JavaScript code with MVP

2011 August 3

JQuery is a wonderful tool for JavaScripters and it’s been around for years, showing its true power and simplicity. But even jQuery falls short of solutions when the web site/application grows in size and complexity.

After writing a lot of JavaScript code on a web application, it becomes more obvious that the direct way of doing it is not the right way and ends up having code no one understands anymore.

So how to get rid of the Javascript clutter? Enter MVP!

Actually it’s all about separation of concerns. The problem with most code is it mixes 3 aspects:

  1. AJAX calls
  2. DOM manipulation
  3. Event handling

We can call them the Model, the View and the Presenter and instead of putting them together in one place, we can define separate objects for each.

Let’s see how the code may look like on a page that performs an async search.

THE MODEL


    var model = {
        searchWebsite: function(searchQuery, handlers) {
            $.ajax({
                url: "/search",
                type: "POST",
                data: "q="+searchQuery,
                success: handlers.success,
                error: handlers.error
            });
        }
    }
        

This guy is only responsible for sending the search query to the server, to the right URL and using the right method (POST). He will not interpret the response to populate the page with the results, for example. That’s not his problem. He only manages server calls.

The View

The View, on the other hand, he knows about how things are arranged on the page. He’s really good at finding stuff in the DOM tree, moving things around, changing styles and so on. In our example, he’ll be responsible for installing a handler on the search button, providing the search query and, later on, putting the results where they belong.


    var view = {
        installSearchHandler: function(searchHandler) {
            $("#searchButton").click(function () {
                var searchQuery = $("#searchQuery").val();
                searchHandler.call(this, searchQuery);
            });
        },

        showSearchResults: function(searchResults) {
            for (aResult : searchResults) {
                // create a DOM node and show the aResult in it
                $("#searchResults").append('<p>'+aResult.title+'</p>');
            }
        },

        showSearchError: function(error) {
            $("errorDiv").show();
            $("#errorDiv #errorMessage").html(error);
        }
    };
        

The Presenter

This guy is responsible for putting things together, he’s the middle man, deciding the flow of the page. The Presenter will install the listeners into the View and send requests to the Model to retrieve or send data from and to the server. An important aspect of the Presenter is that he’s aware of the other 2.


    var presenter = {
        init: function() { // start from here (page load)
            view.installSearchHandler(this.search);
        },

        search: function(searchQuery) {
            model.searchWebsite(searchQuery, {
                success: function(data) {
                    view.showSearchResults(data.searchResults);
                },
                error: function(data) {
                    view.showSearchError(data);
                }
            });
        }
    };
        

Starting up this chain of objects

The presenter.init() is supposed to start up this whole deal, from there on, he’ll handle everything and delegate tasks to the view or model as appropriate. We can use jQuery document ready for this:


    $(document).ready(function() {
        presenter.init();
    });
        

And we’re done!

What’s the gain?

Now that we’ve implemented our search the MVP way, let’s take a moment and look at our accomplishment. Why did we do this? What is our gain? If that’s not obvious, let’s see how the code would have looked without structuring it the way we did:


    $(document).ready(function() {
        $("#searchButton").click(function () {
            var searchQuery = $("#searchQuery").val();
            $.ajax({
                url: "/search",
                type: "POST",
                data: "q="+searchQuery,
                success: function(data) {
                    for (aResult : data.searchResults) {
                        // create a DOM node and show the aResult in it
                        $("#searchResults").append('<p>'+aResult.title+'</p>');
                    };
                },
                error: function(error) {
                    $("errorDiv").show();
                    $("#errorDiv #errorMessage").html(error);
                }
            });
        });
    });
        

Everything is put together in one place, making it harder to read and understand what is all about.

On the other hand, if we look at our presenter, we understand immediately what the page is supposed to do: search by a searchQuery and, in case of success, showSearchResults, in case of error, showSearchError. You notice we used words from the code itself to explain what the page does – that’s because the code sounds more like an English language phrase than cryptic JS code.

In the real world out there, the code grows a lot more, as well as the clutter. Out there, this design will prove even more useful while our counter-example – even worse.