Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to validate date format in a Spring form

I'm new to Spring, and I'm trying to create a form that validates a date format (i.e.: it accepts only dates with the format "MM/dd/yyyy" if the user puts "mm-dd-yyyy" it should show an error message).

How can I achieve this with Spring?

I have read a lot of posts and answers like this and this, that recommend using the @InitBinder in the controller (I tried but couldn't make it work btw). But what if I have a form with different dates? or if my controller manages multiple post request from different forms, and each one requires different dates validations?

Currently I have this form:

<form:form action="getReportFile.html" commandName="staticReportForm">
            <table>
                <tr>
                    <td>Reports:</td>
                </tr>
                <tr>
                    <td><form:select path="report" items="${staticReports}"/>                        
                    </td>
                </tr>
               <tr>
                   <td>Date (MM/DD/YYYY) (empty for most recent possible):<FONT color="red"><form:errors
                                path="date" /></FONT></td>
               </tr>
               <tr>
                   <td><form:input path="date" /></td>
               </tr>
               <tr>
                   <td><input type="submit" value="Submit" /></td>
               </tr>
           </table>            
       </form:form>

And this would be the bean backing the form (the @DateTimeFormat annotation only make it work if you put the correct format):

public class StaticReportForm {
        @NotEmpty        
        private String report;    
        @DateTimeFormat(pattern="MM/dd/yyyy")
        private Date date;

    public String getReport() {
        return report;
    }

    public void setReport(String report) {
        this.report = report;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }


}
like image 956
Christian Vielma Avatar asked Feb 05 '13 21:02

Christian Vielma


1 Answers

I don't know if there is a direct way to do it in spring, but the way I have done it is a combination of jQuery's DatePicker and InitBinder.

In the JS side, you create a:

<form:input cssClass="datepicker" path="someProperty" readonly="true" />

Then in the JS:

$(document).ready(function() {
    $('.datepicker').datepicker();
});

On the Controller side, create a method like thus:

@InitBinder
public void initBinder(WebDataBinder binder) {
    SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
    sdf.setLenient(true);
    binder.registerCustomEditor(Date.class, new CustomDateEditor(sdf, true));
}

From here you can create typeMismatch messages in your resource bundle to create a nice, pretty message. The user will not be able to manually type into the field, but instead will only be able to use the jQuery DatePicker, which will format the date as needed (I think the default is MM/dd/yyyy). In the case where they DO manage to enter a date, Spring will use the configured CustomEditor to convert the String from the view to the Date expected. If it fails, you get a error in the BindingResults (if you include it in your method signature). You can customize this method, as I stated before, but setting up a custom typeMismatch message in your resource bundle.

Edit: Adding additional details as my explaination above was obviously not clear enough...

First, create a Bean or something to act as your Model Attribute (what you send back and forth from View to Controller). Ensure it has at least one date in it.

public class SomeBean {
    private Date someDate;
    // ...additional properties, getters, setters...
    public Date getSomeDate() { return someDate; }
    public void setSomeDate(Date date) { somedate = date; }
}

Now you need a controller. I like to make my Model Attributes session attributes via the

@SessionAttribute.
@Controller
@RequestMapping("/somePath")
@SessionAttributes({"someFormBean"})
public class SomeController {
    /**
     * Handler method
     */
    @RequestMapping()
    public String defaultView(@ModelAttribute("someFormBean") SomeBean someFormBean, Model uiModel) {
        // Do something in your default view
        return "someDefaultView";   // Assuming you have a ViewResolver, like JspViewResolver or Tiles
    }

    /**
     * Submission Handler method
     */
    @RequestMapping(method = RequestMethod.POST
    public String submit(
        @ModelAttribute("someFormBean") SomeBean someFormBean, 
        BindingResult bindingResults,
        Model uiModel) {
        // bindingResults will have your errors from binding
        if(bindingResults.hasErrors()) {
            return "errorView";
        } else {
            return "successView";
        }
    }

    /**
     * Will be called to create your Model Attribute when its missing
     */
    @ModelAttribute("someFormBean")
    public SomeBean createSomeBean() {
        return new SomeBean();
    }

    /**
     * register property editors
     */
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
        sdf.setLenient(true);
        binder.registerCustomEditor(Date.class, new CustomDateEditor(sdf, true));
        // You can register other Custom Editors with the WebDataBinder, like CustomNumberEditor for Integers and Longs, or StringTrimmerEditor for Strings
    }   
}

Then you need some view ("someDefaultView" above in the controller, my code is JSP in this example, using the Spring JSTL tag library)

<%@ taglib prefix="c"       uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt"     uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn"      uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="form"    uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="spring"  uri="http://www.springframework.org/tags" %>
<html>
    <head>
        <script type="text/javascript" src="/resources/jquery/1.7.1/jquery-1.7.1.min.js"></script>
        <script type="text/javascript" src="resources/jquery.ui/1.8.13/jquery.ui.min.js"></script>

        <script type="text/javascript">
            $(document).ready(function() {
                $('.datepicker').datepicker();
            };
        </script>
    </head>
    <body>
        <form:form modelAttribute="someFormBean">
            <form:input cssClass="datepicker" path="someDate" readonly="true" /><br />
            <form:errors path="a"/><br /><br />
            <input type="submit" value="Submit" />
        </form:form>
    </body>
</html>

Again, I would suggest Google'ing Spring Init Binders, cusomizing binding errors (typeMismatch), and JSR 303 for additional options for validation, most of when is well documentet here. Also, if you don't want the error below to the field, as I have done here there are ways to iterate all the errors in one spot, like putting all the errors at the top of the page. Its very configurable, and I could type probably another 20 pages worth going over all of it in depth. This should be plenty to get you started in finding good examples and documentation.

like image 131
CodeChimp Avatar answered Sep 20 '22 23:09

CodeChimp