Thursday, June 26, 2014

Porting to Foundation: Handling HTML5

Introduction

This post will focus on extending the Spring form tag lib.  As I discussed in my prior post we need to enhance the attribute handling of this library to utilize HTML5 input facilities in our application.

Most of the HTML5 attributes are traditional name value pairs and with the dynamic attribute handling provided by the current tag library this is sufficient for most of the new attributes. However there are a couple of attributes, specifically 'required' and 'autofocus', that we need to omit from the rendering if not needed.  For example if we attach the 'required' attribute and set it to false on an input element (required="false") that field will still be required.  To correctly generate HTML5 requires that we extend the Spring WebMVC form tag library to handle these attribute.

Fortunately, we will only need to extend a limited subset of the tags from the current form tag library.  The 'required' and 'autofocus' attribute handling needs to be injected into input, textarea, checkbox and select tags.  Implementing this functionality is not exceeding difficult.  To implement we need to;
  • Define the new tag library definition file (TLD file) containing the definitions of the tags we are extending.
Then for each tag we must;
  • Provide an implementations of the extended behavior.
  • Modify the tag definition to reference the extended class and add the new attributes we are extending.
  • And finally modify the tagx file to use the extended tag.

Defining the Tag Library File

The first step is to get the newly custom tag library functional is the creation of the tag library file definition.  This piece ties the Java implementation that we need to provide later to the tags in the JSP.

The tag definition library must be placed in the WEB-INF folder.  For the purposes here we need to make a copy of the Spring form tag library definition file and edit it to fit our needs.  A complete explanation of custom tag libraries is out of our focus here.  As mentioned before only the input, checkbox, select and textarea tags need to be extended so the first edit on this file should be to remove all the other tags to reduce its size.

Provide the Extended Tag Behavior

Then for each of the remaining tags we need to change the tag-class to reference the extended Java implementation of that class.  For example here is the Java class for the input tag;;

package com.springsource.petclinic.tag;

package com.springsource.petclinic.tag;

import javax.servlet.jsp.JspException;

import org.springframework.web.servlet.tags.form.InputTag;
import org.springframework.web.servlet.tags.form.TagWriter;

public class HTML5InputTag extends InputTag {

 private static final long serialVersionUID = 1L;

 private String required;

 private String autofocus;

 private String min ;
 
 private String max ;
 
 public String getAutofocus() {
  return autofocus;
 }

 public String getMax() {
  return max;
 }

 public String getMin() {
  return min;
 }

 public String getRequired() {
  return required;
 }

 public void setAutofocus(String autofocus) {
  this.autofocus = autofocus;
 }

 public void setMax(String max) {
  this.max = max;
 }

 public void setMin(String min) {
  this.min = min;
 }

 public void setRequired(String required) {
  this.required = required;
 }

 @Override
 protected void writeOptionalAttributes(TagWriter tagWriter) throws JspException {
  if (TextUtils.isTrue(required)) {
   writeOptionalAttribute(tagWriter, "required", "true");
  }
  if (TextUtils.isTrue(autofocus)) {
   writeOptionalAttribute(tagWriter, "autofocus", "true");
  }

  if (!TextUtils.isEmpty(min)) {
   writeOptionalAttribute(tagWriter, "min", min.trim());
  }
  
  if (!TextUtils.isEmpty(autofocus)) {
   writeOptionalAttribute(tagWriter, "max", max.trim());
  }

  super.writeOptionalAttributes(tagWriter);
 }

}


Here the custom input extends the Spring provided implementation adding a collection of new attributes and exposing it by offering getter and setter methods.  To handle the rendering the class overrides the writeOptionalAttributes method.  This method is invoked during the rendering of the tag and allows the attributes to be optionally injected into the resulting markup.

With the extension of the input class I have also included handling of the 'min' and 'max' attributes.  Unlike the 'required' and 'autofocus' attributes these attributes are injected into the markup only if not null or empty strings.  Adding these attributes allows streamlining of those attributes in the input.tagx file.

Modify the Tag Definition

Now we need to modify the tags definition in the tag definition file (TLD) that we created earlier.  There are two updates to the tag that need to made.  First the tag-class element needs to reference our newly implemented tag extension class.  And second we need to add the attribute definitions for the new attributes being handled.

Here is the resulting tag definition for the input tag;

 <tag>
  <description>Renders an HTML 'input' tag with type 'text' using the bound value.</description>
  <name>input</name>
  <tag-class>com.springsource.petclinic.tag.HTML5InputTag</tag-class>
  <body-content>empty</body-content>

  <!-- a bunch of attribute definitions omitted here >

  <attribute>
   <description>HTML5 Attribute</description>
   <name>required</name>
   <required>false</required>
   <rtexprvalue>true</rtexprvalue>
  </attribute>
  <attribute>
   <description>HTML5 Attribute</description>
   <name>autofocus</name>
   <required>false</required>
   <rtexprvalue>true</rtexprvalue>
  </attribute>
  <attribute>
   <description>HTML5 Attribute</description>
   <name>min</name>
   <required>false</required>
   <rtexprvalue>true</rtexprvalue>
  </attribute>
  <attribute>
   <description>HTML5 Attribute</description>
   <name>max</name>
   <required>false</required>
   <rtexprvalue>true</rtexprvalue>
  </attribute>
  <dynamic-attributes>true</dynamic-attributes>
 </tag>


Modify the input.tagx File

The last part of the implementation is to modify the tagx file to use the extended tag.  Doing this requires that we change the 'form' namespace declaration to refer to the extended tag library definition rather then the Spring provided one.

Here is the root element definition from the input tagx file as generated by Roo;

<jsp:root xmlns:c="http://java.sun.com/jsp/jstl/core" 
 xmlns:fn="http://java.sun.com/jsp/jstl/functions" 
 xmlns:spring="http://www.springframework.org/tags" 
 xmlns:form="http://www.springframework.org/tags/form" 
 xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0">



And here is the modified element;

<jsp:root xmlns:c="http://java.sun.com/jsp/jstl/core" 
 xmlns:fn="http://java.sun.com/jsp/jstl/functions" 
 xmlns:spring="http://www.springframework.org/tags" 
 xmlns:html5="urn:jsptld:/WEB-INF/html5-form.tld"
 xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0">


Now in the input element we have to refactor the form namespace elements to use the new namespace.  It is also here where the new functionality of the tag library can now be utilized.  In the original application the form:input tag code has;

              <form:input id="_${sec_field}_id" path="${sec_field}" disabled="${disabled}" />

Now in that code we can write;

<html5:input id="_${sec_field}_id" type="${type}"
 path="${sec_field}" disabled="${disabled}"
 required="${required}" pattern="${validationRegex}"
 minLength="${min}" maxLength="${max}" min="${minDecimal}"
 max="${maxDecimal}" step="${step}" />


The application tagx files can now take full advantage of the new HTML5 goodies, including the 'required' attribute..  What remains to be done at this point is providing similar implementations for all the input controls that we identified earlier.  With mix-ins this would be a snap but unfortunately right now we have to maintain some similar code.

This leads us to the end of this section.  My next post will proceed from here and continue the implementation for HTML5 tags for the pet clinic application.

No comments:

Post a Comment