Web forms with Java: AngularJS and other approaches
After learning about AngularJS a couple of months ago, I started using it on new Java web projects, and that has been a great pleasure. If you haven’t worked with AngularJS yet, you may be wondering what the hype is all about and whether or not it’s a thing worthwhile of investing your time in. In this blog, I’d like to put some of the merits of AngularJS in the spotlights, by comparing it to some other approaches for web application programming in the Java world.
To this end, I’ve picked a simple yet common test case: a web form with a couple of fields that need to be validated prior to actually processing the form. The fields are “first name” (with a minimum length of 3 and a maximum length of 20) and “age” (an integer with a minimum value of 0 and a maximum value of 130). Both fields are required. I’ve implemented this case in 4 ways:
Of course, option number 1 is there for setting the stage only. Non-Ajax forms might have been a reasonable option in 2004, but they’re definitely not in 2014. It’s good though, to review this case, as it deepens the understanding of the pros and cons of the other approaches.
For all options, there’s a Maven project implementing the case, containing all sources. You can download a zip containing all four projects here. The projects have been tested on Tomcat 7 (Servlet API 3.0) and JDK 6. Technical details about setting up the right dependencies and the correct web.xml will be omitted from the discussion below for brevity, please find those details in the Maven projects.
Plain old HTML form (non-Ajax)
The most basic HTML representation of the form we would like to create is something like this:
<form method="POST"> <div> <label>First name:</label> <input name="firstName" type="text"> </div> <div> <label>Age:</label> <input name="age" type="text"> </div> <div> <input type="submit" value="Submit the form"> </div> </form>
The above HTML code works as-is; the browser understands it and when the user presses the submit button, it will send the values of the firstName
and age
inputs to the server by POST-ing it to the current URL using the application/x-www-form-urlencoded
encoding. The name
attribute of the inputs is the key used for the data. In response to the POST, the browser expects a new complete HTML document, or maybe an error or redirect. But it won’t update the current document, this is all pre-Ajax technology.
Of course, we will need to build something server-side to pick up the data and further process it. Spring MVC is a fine choice for doing this in Java. We can set up a controller method like
@RequestMapping(method = RequestMethod.POST) public String postForm(Model model, @RequestParam("firstName") String firstName, @RequestParam("age") String age) {
Within the controller method, we can do whatever we need to do with firstName
and age
, and, if validation passes, redirect the user to a “thank you” view. In case validation fails however, we need to let the user know what went wrong. Also, we don’t want to lose the values that the user already entered, which is what would happen if we would simply reload the form. Therefore, our form won’t be plain HTML at the server side, but rather a JSP file that will produce HTML containing server-provided data. (Of course, any view/template technology other than JSP may be used to the same effect.) In the JSP case, it will look something like this:
<ul> <c:forEach items="${messages}" var="message"> <li><c:out value="${message}"></c:out> </c:forEach> </ul> <form method="POST"> <div> <label>First name:</label> <input name="firstName" type="text" value="${firstName}"> </div>
Through the messages
variable, validation messages can be shown to the user. The value="${firstName}"
makes sure that the previously entered value is present when the form is loaded again after validation has failed.
Is this a viable way to create forms for today’s web applications? No. The interaction that can be implemented this way is limited to loading a completely new browser document. That is too blunt to create a decent user experience, especially as the form and the surrounding application get more complex. What we need to be able to do, is to selectively process and update the current page using Ajax technology. The other 3 models we’ll review below can all do that, but achieve it in different ways.
JSF
Java Server Faces (JSF) is the user interface standard within Java EE. Its original versions didn’t natively include Ajax support, but this was available through 3rd party libraries. Since version 2, Ajax-support is included as a native feature of JSF.
JSF is a big and complex beast, but I think the following points really cover its essential characteristics for our current purposes:
- JSF is a component-oriented framework, like Wicket and Tapestry. The means that pages are defined by trees of server-side components. The server will generate HTML from this component-tree as part of the process of rendering a page. Although page definitions may be HTML-like or contain fragments of HTML, the responsibility for creating HTML (and the JavaScript to go with it) is fundamentally placed within the
framework and away from the developer. - JSF provides two-way data binding between Java Beans and controls in page definitions. By contrast, in simple JSP pages, server-side attributes may be used to control what is displayed, but the reverse path from browser back to server is not covered. This should be handled by the developer in a controller. In JSF, the framework handles the full, two-way data binding. This includes the process of conversion and, to some extent, validation.
- By default, and due to its history, JSF will do nothing Ajax-like. We will have to tell it to do so explicitly.
The JSF version of our form looks like this:
<h:form> <h:messages/> <div> <label>First name:</label> <h:inputText value="#{form.firstName}" label="First name" required="true"> <f:validateLength minimum="3" maximum="20" /> </h:inputText> </div> <div> <label>Age:</label> <h:inputText value="#{form.age}" label="Age" required="true"> <f:convertNumber groupingUsed="false" integerOnly="true"/> <f:validateLongRange minimum="0" maximum="130" /> </h:inputText> </div> <h:commandButton value="Submit the form" action="#{form.submit}"> <f:ajax execute="@form" render="@form"/> </h:commandButton> </h:form>
The plain HTML input
elements have been replaced by JSF’s h:inputText
components. All conversion and validation that had to be done programmatically in the previous case, is now done declaratively (but still processed server-side). Validation results are being shown in the h:messages
component. The h:commandButton
takes care of submitting the form and thanks to the f:ajax
child element it will do this in the Ajax way. The execute
attribute specifies what part of the data should be submitted, and the render
attribute specifies what part of the document should be re-rendered; in this case, we simply want to submit and re-render the entire form.
Thanks to all the work that the JSF framework does for us, the actual backing bean is quite simple:
@ManagedBean(name = "form") @ViewScoped public class FormBean implements Serializable { private String firstName; private Integer age; public Object submit() { // Process the form. return "/confirm.xhtml"; } // Getters and setters omitted for brevity. }
So, is this a good programming model for the real world cases that of course are way more complex than our example? In my view, it depends. There are two ways in which (frameworks like) JSF can really boost productivity:
- As we have seen in our example, there is hardly any boiler plate code to write. No data binding code, no conversion code, no validation code. No client-side JavaScript at all. The developer can focus on coding business logic in Java. Generally, that’s a Good Thing.
- Especially if we combine JSF with 3rd party component libraries like PrimeFaces, ICEfaces or Oracle ADF Faces, very rich and complex controls can be used and combined in pages, with very little effort from the developer. IDE’s play well with the component-oriented nature, so JSF pages may be composed from controls in a drag-and-drop manner.
Unfortunately, there are some major drawbacks to the approach as well:
- All these components and other declarative stuff like converters and validators work really well and are really simple as long as you can afford to stay with what is already available. As soon as you need to have something that is not readily available, or need real control over your HTML/JavaScript, things start to change. You will have to write custom-components, custom-converters, use explicit component binding rather than the automatic data binding, etc. Lots of the elegance and simplicity of the model gets lost in this case.
- JSF has been a framework for generating full pages from a component tree, and has been extended to support Ajax by generating partial page updates from the component tree. This architecture has negative consequences for performance in several ways. Server-side memory consumption is high because the component tree has to be kept in memory while a user is Ajax-interacting with a page. Network bandwidth consumption is high as well because entire chunks of HTML get retransmitted rather than just the data.
The way in which the pros and cons work out really depends on the nature of the application. If you’re building an internal, employee-facing application, with several hundreds to thousands of users, complex web forms, but no overly detailed look-and-feel requirements, JSF or a similar framework may work out great. If you need to scale to large numbers of users and need to have full control over the HTML and the user interaction, then you’ll probably need a different approach that provides more control.
jQuery
The general approach to having full control of an Ajax-enabled web form, is to write the JavaScript code needed to Ajax-enable it, yourself, rather than have it generated by JSF or a similar framework. Writing “raw” JavaScript code without using any framework would be hard because different browsers have somewhat different APIs for the same functionality (like sending out HTTP requests and handling browser events), and because the standard document API is a bit clumsy. Luckily, libraries have been developed that provide a clean, browser-independent API, and jQuery has become the most popular choice for this.
If we build our sample form in this way, we can start with a plain HTML file (no JSP):
<html> <head> <title>Webforms demo - jQuery</title> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script> <script src="js/form.js"></script> </head> <body> <h1>Please enter your personal information:</h1> <ul id="messages"> </ul> <div> <label>First name:</label> <input id="firstName" type="text"> </div> <div> <label>Age:</label> <input id="age" type="text"> </div> <div> <input id="submit" type="button" value="Submit the form"> </div> </body> </html>
Compared to the plain old HTML form, a couple of things have changed. We include some JavaScript; both the jQuery framework, and our own code. The form
as such is gone, and we no longer have an input type="submit"
, since we’re no longer depending on the browser “understanding” the form. All inputs have an id
rather than a name
, since the id
is the best way to identify them in our JavaScript code.
In this approach, we will need to decide upon a ‘protocol’ for sending data between the client and the server. Let’s choose the following:
- When submitting the form, the browser will POST a JSON object with two fields, firstName and age.
- If everything is ok, the server will return a status code 204 (‘no content’), without any content (as implied by the status code :)). The client will then perform the redirection to the ‘thank you’ page.
- If something is wrong, the server will return a status code 400 (‘bad request’), with a JSON-encoded list of messages to display to the user.
The jQuery/JavaScript code to accomplish this looks like this:
$(document).ready(function() { $('#submit').click(function() { var data = { firstName: $('#firstName').val(), age: $('#age').val() }; $.ajax({ type: 'POST', url: './form.do', contentType: 'application/json; charset=utf-8', dataType: 'json', data: JSON.stringify(data), success: function(data, textStatus, jqXHR) { window.location.replace('./confirm.html'); }, error: function(jqXHR, textStatus, errorThrown) { if(jqXHR.status == 400) { var messages = JSON.parse(jqXHR.responseText); $('#messages').empty(); $.each(messages, function(i, v) { var item = $('<li>').append(v); $('#messages').append(item); }); } else { alert('Unexpected server error.'); } } }); }); });
The reader familiar with jQuery may note that this is not the canonical way of submitting a form in jQuery. You could use the regular HTML form
tag, and then use jQuery’s serialize
function when sending the form. The data will then sent in the application/x-www-form-urlencoded
encoding. My experience is that on real world projects, you need some more control on the mapping of HTML control values to the request that’s actually sent to the server, which is why I’ve demonstrated this JSON-based approach.
The Java part can be built with Spring MVC like in the first case; but our controller has a slightly different signature:
static class FormData { public String firstName; public String age; } @RequestMapping(method = RequestMethod.POST) @ResponseBody public ResponseEntity<?> postForm(@RequestBody FormData formData) { List<String> messages = new ArrayList<String>(); messages.addAll(validateFirstName(formData.firstName)); messages.addAll(validateAge(formData.age)); if (messages.isEmpty()) { /* Process the form. */ return new ResponseEntity<String>( "", HttpStatus.NO_CONTENT); } else { return new ResponseEntity<List<String>>( messages, HttpStatus.BAD_REQUEST); } }
Spring MVC will delegate the conversion between JSON objects and Java objects to Jackson. It does a pretty good job at this with default settings, which is why there is no JSON-related code in our example. Our example uses programmatic, server-side validation. If desired, this could easily be changed to declarative server-side validation (for instance, using JSR-303). Also, validation could be done client-side by doing checks before sending out the HTTP request.
Generally speaking, this programming model is really nice. We have full control over the HTML, the interaction, and the way we divide tasks between client and server. There are no obvious performance bleeding points. The server side code is straightforward and doesn’t have a lot of boilerplate.
However, there’s one place where the model hurts: the JavaScript/jQuery code. This code is doing several things at once:
- It’s handling the communication with the server through the
$.ajax
call. - It’s binding the values of HTML input elements to JavaScript object values (
$('#firstName').val()
); this is a simple job in our simple example, but this becomes much more code in a real-world example. - It’s responsible for binding an event handler to the click event (
$('#submit').click(function() {
). - It’s generating new HTML based on the server response (
$('#messages').append($('<li>').append(v))
).
This combination of responsibilities is by itself a big maintainability risk as our web form becomes more realistic and complex. To make matters worse, we have no real way to modularize and manage our JavaScript code base as its complexity grows. This is where AngularJS comes in.
AngularJS
Our AngularJS version of the form has exactly the same Java backend code as the jQuery version. The only thing that changes is the HTML and JavaScript code. What AngularJS allows us to do is to use the basic jQuery programming model we just reviewed, with all of its advantages in the areas of flexibility, performance and server side simplicity, while at the same time providing us with really powerful tools to keep the JavaScript side of things manageable. Let’s have a look at the AngularJS versions of the HTML en JavaScript files we just saw:
<html data-ng-app="FormApp"> <head> <title>Webforms demo - AngularJS</title> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.12/angular.min.js"></script> <script src="js/form.js"></script> </head> <body data-ng-controller="FormController"> <h1>Please enter your personal information:</h1> <ul> <li data-ng-repeat="message in messages">{{message}}</li> </ul> <div> <label>First name:</label> <input data-ng-model="data.firstName" type="text"> </div> <div> <label>Age:</label> <input data-ng-model="data.age" type="text"> </div> <div> <input data-ng-click="submit()" type="button" value="Submit the form"> </div> </body> </html>
var formApp = angular.module('FormApp', []);- formApp.controller('FormController', [ '$scope', '$window', '$http', function($scope, $window, $http) { $scope.messages = []; // AngularJS will populate this object with input // values based on the data-ng-model mappings. $scope.data = {}; $scope.submit = function() { $http({ method: 'POST', url: './form.do', data: $scope.data }). success(function(data, status, headers, config) { $window.location.replace('./confirm.html'); }). error(function(data, status, headers, config) { if(status == 400) { $scope.messages = data; } else { alert('Unexpected server error.'); } }); }; }]);
Here, we see the programming model of AngularJS in action. The HTML file gets enriched with AngularJS-specific attributes (like data-ng-model
on the inputs and data-ng-click
on the button. The attributes are used by AngularJS to do the binding between the document and the JavaScript. Also, we may use the {{ ... }}
construct to include JavaScript variable values. This is somewhat like the JSP ${...}
construct, except that it’s running client-side and it’s completely dynamic: if the value of the JavaScript variable changes at any point in time, so will the HTML.
There is no JavaScript code for reading input values, for writing HTML, or for registering event handlers. All of that stuff is taken care of by AngularJS behind the scenes. The total reduction in lines of code may seem limited in this example, but that’s because there are so few fields. In real-world projects, the reduction in boilerplate code is huge.
In addition to the boilerplate elimination, we see some other mechanisms here that help manage the JavaScript complexity. AngularJS supports the notion that we sometimes have pieces of interaction/logic that get instantiated multiple times on a single page. This is what JSF and competitors model as a component. In the AngularJS JavaScript world, this is modelled as a controller
with a particular $scope
. The $scope
is made available to the controller function by AngularJS’s dependency injection mechanism. This mechanism is used more broadly to wire together your own application code with AngularJS’s core functions and third party libraries.
AngularJS is a really powerful framework that allows the flexibility of the jQuery-approach to Ajax web forms, while keeping a manageable JavaScript code base as your application grows. In my opinion, it’s an excellent choice for a large class of web applications.