guazo
guazo

Reputation: 1

Spring web flow model binding

I am encountering some issues with binding model properties in spring web flow. When I use <form:input path="${propertyName}" cssClass="form-control" />, the generated HTML tag comes out as <input class="form-control" type="text" value=""/> and the HTML name attribute is completely omitted. Additionally, if I try to initialize the property in the model, I get a runtime property not found exception. I am using Spring 4.1.2 with spring web flow 2.4.1.

The weird thing is I have two select fields on this model that are being bound correctly. But this issue is happening for text properties that are either scalar or nested object properties.

Does anyone know why the HTML name attribute is not being generated by the spring form:input tag even though I am specifying the path attribute? Thanks

This has been puzzling me now for more than a week. Will really appreciate any help. Not sure why I am not getting any binding working. I even created a stripped down project and binding is still not working. Here's my web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xmlns="http://java.sun.com/xml/ns/javaee" 
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 
         id="WebApp_ID" version="3.0">
  <display-name>Test</display-name>  
  <servlet>
    <servlet-name>DispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:DispatcherServlet-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

And here's my DispatcherServlet-context.xml:

<beans  xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
                            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
                            http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">

    <mvc:annotation-driven />
    <context:component-scan base-package="com.test.project" />

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>

</beans>

HomeController.java:

package com.test.project.controller;

import java.util.Map;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.test.project.Customer;

@Controller
public class HomeController {

    @RequestMapping("/")
    public String welcome(Model model) {        
        Customer customer = new Customer();
        model.addAttribute("customer", customer);       
        return "welcome";
    }

    @RequestMapping(value = "/checkbindings", method = RequestMethod.POST)
    public String checkcusts(

        @ModelAttribute("customer") Customer customer, BindingResult result, Map<String, Object> model) {

        System.out.println("CheckCusts: " + customer.toString());
        return "redirect:/";
    }

}

Customer.java:

package com.test.project;

public class Customer {

    private String customerId;
    private String firstName = "First";
    private String lastName = "Second";

    public String getCustomerId() {
        return customerId;
    }
    public void setCustomerId(String customerId) {
        this.customerId = customerId;
    }
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String toString() {
        return String.format("Customer[ID=%s, firstName=%s, lastName=%s]", customerId, firstName, lastName);
    }

}

welcome.jsp:

<%@ include file="/WEB-INF/jsp/include.jsp"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Welcome</title>
</head>
<body>

        <form:form modelAttribute="customer" action="checkbindings">        
            <div>
                ID: <form:input path="${customerId}" />
            </div>
            <div>
                FName: <form:input path="${firstName}" />
            </div>          
            <div>
                LName: <form:input path="${lastName}" />
            </div>

            <div><form:button type="submit">SUBMIT</form:button></div>
        </form:form>


</body>
</html>

And when I post the form, this is what I see printed out for the submitted values:

CheckCusts: Customer[ID=null, firstName=First, lastName=Second]

The behaviour is really weird. First, I am not getting the values I enter on the form. Previously, when I tried to initialize the values as I am now doing, I used to get the error "First" is not a valid property which led me to think the framework somehow interpreted the initialized value as a bean property. I am not getting that error any more. Why is is that the binding is not working? Why can't I see the values I entered on the form? Am I missing something? Any help is greatly appreciated. Thanks

UPDATE

There is something seriously wrong going on here. If I change the welcome.jsp's markup to the following:

<form:form modelAttribute="customer" action="checkbindings">    
    <div>
        ID: <form:input path="${customerIdX}" />
    </div>
    <div>
        FName: <form:input path="${firstNameX}" />
    </div>          
    <div>
        LName: <form:input path="${lastNameX}" />
    </div>

    <div><form:button type="submit">SUBMIT</form:button></div>
</form:form>

Take note of the wrong property names with the X at the end, the form still renders without errors. I would expect errors here when the binding is invoked. The HTML source is below:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Welcome</title>
</head>
<body>  
        <form id="customer" action="checkbindings" method="post">   
            <div>
                ID: <input type="text" value=""/>
            </div>
            <div>
                FName: <input type="text" value=""/>
            </div>          
            <div>
                LName: <input type="text" value=""/>
            </div>

            <div><button type="submit" type="submit" value="Submit">SUBMIT</button></div>
        </form>


</body>
</html>

Notice the form:input tag does not generate the equivalent HTML name attributes and I guess that's why no binding is taking place. The same behavior however happens even with the correct property names. Is this a Spring bug?? This is really frustrating now.

Upvotes: 0

Views: 2928

Answers (1)

Doug Breaux
Doug Breaux

Reputation: 5105

You don't use ${propertyName} for the path, you use just propertyName. That is:

<form:input path="customerId" />

${customerId} is trying to use the value of a JSP EL variable named 'customerId' as the bean's field name. Since you haven't set any EL variable named 'customerId', its value is blank.

Upvotes: 1

Related Questions