In current post I am going to explain how to implement nice web form using HTML 5 and CSS 3 effects, and how can be integrated into Spring MVC web application.
First thing to do is create a Spring MVC skeleton project using STS Spring Templates. In fact you can use any other method but I prefer to use a standard template.
Next step is create an
HTML 5 web structure, but exists some online applications that can do this for you, hence I am going to
HTML 5 Boilerplate site, and create a custom
HTML 5 template.
Then open downloaded
zip and copy required structure into previous generated project.
Css and Js folders into resources directory, and rename index.html to index.jsp and copy it to views directory.
Now open servlet-context and add mvc:resources tag for serving static resources.
If you deploy application a white screen should be showed but without any client/server error.
Let's start creating next html form.
Now open index.jsp and remove div with id main and create next form:
And now if you run again, you should see something like:
Add
@import url(http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz); on top of
CSS file
.This will make a nice font available from
Google Fonts directory.
Now it is time to start applying CSS for creating a better layout. Open style.css and modify/create next elements:
And now your form should look with next layout:
See that new font is not used, simply find body,button,input,select,textarea entry and remove font-family property.
Better than before, see how only modifying CSS, form layout is modified.
And now let's start with CSS3 magic.
First trick is adding shadows and gradients to form.
And next lines into input, textarea style:
where -moz prefix is used for Gecko based browsers, and -webkit is used for Webkit browsers.
Next lines add some mouse over effects into form elements.
And to finish applying styles, we are going to modify submit button.
Remove input[type="submit"] from button and input style.
And add next lines into CSS file:
CSS magic is over, now it is time to start with server side.
If you look careful Spring taglibs, you will see that you cannot create for example email input types directly or URL types to cite a few. So I decided to use a new template engine called Thymeleaf, that offers a really nice approach for integrating HTML 5 with Spring MVC applications.
The main goal of Thymeleaf is to provide an elegant and well-formed way of creating HTML 5 templates. Its Standard and SpringStandard dialects allow you to create powerful natural templates, that can be correctly displayed by browsers and therefore work also as static prototypes. This is possible due the absence of taglibs like <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>.
First thing to do is change index.jsp to index.html, and change ViewResolver to Thymeleaf ViewResolver:
First add Thymeleaf dependencies into pom and then modify servlet-context.xml to register TempateResolver, TemplateEngine and ThymeleafViewResolver and remove the deault view resolver.
Next step is modeling our form to a model class, and modify Spring Controller to manage form submition.
Model class is not shown because is a simple POJO with form fields. Let's see how controller is modified.
Open HomeController class and create two methods (GET, POST) to deal with HTML5 form. Note that it is a typical Spring Form Controller;
And finally we are going to templarize index.html content, using Thymeleaf engine.
Open index.html and make next changes:
Add Thymeleaf namespace on html tag.
<html xmlns:th="http://www.thymeleaf.org">
Then change form tag with two new attributes, one with object's model name (form backing object) and other one with submition URL.
<form action="#" th:object="${messageInfo}" th:action="@{/}" method="post">
Next thing to do is change each form element with th:value attribute containing backing object property name.
<input type="text" name="name" value="" id="name" placeholder="Your Name" required="required" autofocus="autofocus" th:value="*{name}"/>
And more few changes should be performed:
Use DOCTYPE instead of doctype
At time of writing this post, html5boilporate introduces link tag without closing, so Thymeleaf parser will throw an exception if is executed as is, so let's terminate link tag and meta tags.
And finally last change is required because of xhtml correctness:
<script> tag is surrounded with
CDATA tags. See explanation here:
http://javascript.about.com/library/blxhtml.htm.
Now you can deploy application and your
HTML5+CSS3 form is displayed and data submitted is displayed through server console and form page is reloaded with introduced data. And now I am going to explain what are those strange expressions in form and then we will add a new page.
Inside each
Thymelef attribute four kind of expressions can be used:
- ${...} are Spring EL expressions and are executed on model attributes.
- *{...} are expressions executed on the form backing bean, it is like a pointer to form object (root).
- #{...} are for internationalization.
- @{...} are link expressions to rewrite URLs.
And before jumping to next section try this, instead of opening
index.html from
http://localhost:..... try to open from your drive
file://.... and I suppose you see that that's not a nice prototype because no styling is applied. This happens because we are using
CSS location as:
<link rel="stylesheet" href="css/style.css"/>
HTML file is at
/src/main/webapp/WEB-INF/views/ and this directory does not contain
css folder. And now you can think that I have not told you the true about prototyping with
Thymeleaf, but wait trust me and change that line to:
<link rel="stylesheet" href="../../css/style.css" th:href="@{/css/style.css}"/>
And open again, and wow now you can see it, a real page as one generated by the server. What we have changed is that when a browser opens
HTML file without using template engine (
offline),
href attribute is used, but when page is
online (and requested file acts as a template),
th:href attribute is used.
Listing Messages:
Let's start creating a new
HTML file to display all inserted messages and apply some styling with
CSS.
First add a new
CSS property to apply same design in table.
New CSS 3 property (nth-child) is used so even tr tags of a table will contain different style than odd tr tags.
Then create a file called list.html in same level of index.html:
This is a copy paste of index.html but changing div with id content.
In this case we are using another nice feature of Thymeleaf, collection iteration. With th:each you are iterating over a collection of elements. See that rowStat variable can be used for retrieving column information like number of column. And also see how we are defining default values between td tags, so this file is a prototype too.
Next step is changing our controller, so homeForm (renamed to showResults) method returns a list of messages:
Using a simple list as repository is not a good design, but for teaching purpose is enough. See that view name is set to list, and list of messageForm are sent back with model name used in th:each attribute.
Internationalization:
Next step is internationalizing the application. For this reason we create a message properties file (messages_en_US.properties) into src/main/resources/locale:
Then you must configure Locale beans in Spring context file.
And finally change index.html and list.html static values to internationalized keys using #{} expression:
<th th:text="#{theader.count}"></th>
<label for="name" th:text="#{theader.name}">Name: </label>
And although internationalization is used, web page is still a prototype.
It seems like application is working well, but it has one problem. Try next sequence of events:
1. Add a new message.
2. When list of all messages are shown, refresh the page (F5).
And surprise the same message is added too, so now we have two repeated messages, but you have only inserted one.
To fix this problem we must change our controller and use redirect keyword on create method to redirect to a method of controller that finds all inserted messages.
When a new message is created (create method), a redirect to /list is returned, then showResults method is called, and returns a list with all inserted messages. Remember to change th:action from form tag to /insert too.
And now if you execute the same process as before, no multiple insertions occur.
Validation:
And as final step let's see how to implement validation with Thymeleaf.
First thing is adding Jsr-303 provider into our pom. In this case Hibernate-Validator.
Next step is changing our model by for example creating a length constraint in phone field:
@Length(min=9)
private String phone;
To trigger validation of a @Controller input, you must annotate input arguments with @Valid. So our controller is modified to:
And finally modify form page so when an error occurs, a message is shown.
First a new CSS style is created for showing error message:
See that in previous style we are defining an img folder so new static resource resolution in servlet-context.xml as done with css and js folders must be registered.
And finally in index.html a new div is created for showing errors:
For showing errors, #fields variable is used. This variable is provided by Thymeleaf and contains all errors bound by Validation Framework. Note that star '*' is used as errors function parameters for returning all errors produced by all form fields, but Thymeleaf allows you to specify only one particular field.
Conclusions:
From this post I have learned some interesting points:
- With new HTML5 tags, some Javascript validation (like entering well-formatted email) is not required anymore.
- CSS 3 shadows and gradients properties allow us to create amazing forms without using complicated structures of images.
- Thymeleaf is an amazing template engine, in fact for me a real way for creating prototypes and final code at once.
- Thymeleaf is a young project, and for example does not support jQuery by default, but exists a Thymeleaf dialect that integrate it. I think that in nearest future it will be a template engine to be consider.
Hope you find Thymeleaf useful too.
Download Code.
Music:
http://www.youtube.com/watch?v=MMr1zH8NoEY