Saturday, April 4, 2015

Single Page Web Applications

[Excerpts from my book review published in ACM on March 31, 2015.]

Single page web applications : JavaScript end-to-end

Mikowski M., Powell J., Manning Publications Co., Greenwich, CT, 2014. 432 pp.  Type: Book (978-1-617290-75-6)

Date Reviewed: Mar 31 2015

In the late 1990s, a technology now known as Ajax became prevalent in web applications as a tool for fetching content from a web application server asynchronously rather than having to fetch an entire new Hypertext Markup Language (HTML) page whenever a page update was required. The payload format for data exchange between client and server was predominantly Extensible Markup Language (XML) (AJAX was originally an acronym for Asynchronous JavaScript and XML). Typical Ajax use included fetching portions of a page or data updates from a server, the most famous one being Google Maps, which updated its web page asynchronously in response to the user zooming in or out or dragging to a different portion of the map. Even with the advent of Ajax, for most web applications, significant state changes were represented by distinct pages that had to be loaded from the server. These state changes were typically orchestrated and managed using server-side code and the model-view-controller (MVC) pattern. Herein, the server-side controller determines which model (data) and view (page) to return to the client in response to specific user actions.

In the early 2000s, JavaScript Object Notation (JSON) supplanted XML as a lightweight option for structuring the data to be exchanged between clients and servers. Around the same time, people began developing web applications using what came to be called the single page application (SPA) paradigm. In this approach, the only page that ever loads is the first one when a user loads or reloads the application (via a browser refresh). All other state changes, minor and major, are handled asynchronously and generally managed on the client side using a pattern known variously using terminology that is most simply described as “client-side MVC.” Herein, client-side code is responsible for evaluating user actions, fetching the appropriate data from a server (using Ajax/JSON), and mapping the received data to an appropriate view (a responsibility commonly referred to as data binding). In a sense, an SPA behaves more like a desktop application than a traditional web application.

MVC can be implemented by hand (roll your own), but as your application grows (with perhaps tens of states to manage) it quickly becomes unwieldy to continue to do so. Therefore, just as there are various frameworks/libraries for facilitating server-side MVC, many JavaScript frameworks/libraries have emerged for enabling client-side MVC, including Knockout, Backbone, Ember, and Angular. However, what sets this book apart is that the authors argue against using a client-side MVC framework. Having used several client-side MVC frameworks over the past few years, I can appreciate the stance taken by the authors.

Just a couple of years ago, Knockout and Backbone were considered de facto standards for client-side MVC. Then, almost out of nowhere, came Angular, supported by Google’s seemingly infinite programming and marketing resources. But Angular is new and is still undergoing radical changes from release to release. As a result, documentation is often lagging and there are multiple ways to do the same thing: legacy approaches often co-exist with newer approaches, as if to see what sticks. Furthermore, each of these “automatic two-way data binding” frameworks requires the programmer to accept some rigidity in exchange for convenience.

In case you’re wondering, it’s clear that the authors aren’t “roll your own” advocates. It’s just that they don’t want to invest in an immature or rigid client-side MVC framework. Although Angular is gaining traction, as of now there are no client-side MVC frameworks that can reasonably be termed as mature. As evidence of the authors’ level-headedness, consider their testing approach, detailed in Appendix B. Here, the authors write, “Node.js has many test frameworks that have years of use and refinement. Let’s be wise and use one instead of hacking our own.” The key here is the framework’s maturity. Since there are several reasonably mature JavaScript testing frameworks, the authors do not shy away from using them. After briefly describing jasmine-jquery, mocha, nodeunit, patr, vows, and zombie, the authors decide to go with nodeunit and describe how to set up a JavaScript testing framework for their SPA. Similarly, the authors use jQuery, which is another highly mature JavaScript framework for document object model (DOM) manipulation. Additionally, the authors use a pure JavaScript stack, with Node.js and MongoDB on the server side.

One upside of not using a client-side MVC framework is that you’re left with little choice but to become an expert at JavaScript. This book certainly helps with that as it goes step-by-step, building an SPA end to end. One of the book’s highlights is its overview of JavaScript in chapter 2. The authors do a tremendous job of explaining concepts like variable scoping and variable and function hoisting and closure, and provide new insights into the inner workings of JavaScript, such as the execution context object. As a complement to chapter 2, the authors present a useful set of coding standards in Appendix A.

In closing, here’s a sampling of some of the interesting approaches used in this book:

  • The HTML file is merely a shell with no content at all. All of the HTML is converted to JavaScript strings (using regular expressions) and embedded within the JavaScript code.
  • The use of callbacks is reduced via the use of jQuery global custom events.
  • The book recommends feeling a lot less guilty about using pixels since browsers have started implementing pixels as relative units and pixels are often more reliable than traditional relative units like em or percent.
  • The book recommends testing views and controllers manually (although user interface (UI) testing automation frameworks have matured and I’ve had considerable success with the combination of Protractor, Jasmine, and AngularJS).
  • The authors discourage the use of embedded template libraries like Underscore.js, but encourage the use of toolkit-style template systems such as those provided by Handlebars, Mustache, and Dust.