Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
192 views
in Technique[技术] by (71.8m points)

Spring MVC - form generating new model object instances

I have a simple mvc application, which lists all rules in a DB, and allows a user to select a specific row to delete that rule. The controller has a simple listRules() method which adds two model objects to the ModelMap.

@Controller
public class RulesController {

    @Resource
    private RuleManager ruleManager;

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public ModelAndView listRules() {
        ModelAndView mv = new ModelAndView("/rules"); 
        List<Rule> rules = this.ruleManager.getAllRules();
        ListRulesModel listRulesModel = new ListRulesModel();
        listRulesModel.setRules(rules);
        mv.addObject("listRulesModel",listRulesModel);
        SelectedRuleModel selectedRuleModel = new SelectedRuleModel();
        mv.addObject("selectedRuleModel",selectedRuleModel);
        return mv;
    }

    @RequestMapping(value = "/submit", method = RequestMethod.POST, params = {"delete"})
    public ModelAndView deleteRule(@ModelAttribute("selectedRuleModel") SelectedRuleModel selectedRuleModel,ModelMap model) {
        System.out.println("deleteRule "+selectedRuleModel.hashCode());
        System.out.println("model "+model);
        if(selectedRuleModel.getRuleId()!=null)
            getRuleManager().deleteRule(getRuleManager().getRule(selectedRuleModel.getRuleId()));
        return new ModelAndView("redirect:/");
    }

My model objects are

public class ListRulesModel {

    private List<Rule> rules = null;

    public List<Rule> getRules() {
        return rules;
    }

    public void setRules(List<Rule> rules) {
        this.rules = rules;
    }
}

and

public class SelectedRuleModel {

    @NotNull
    private Integer ruleId = null;

    public Integer getRuleId() {
        return ruleId;
    }

    public void setRuleId(Integer ruleId) {
        this.ruleId = ruleId;
    }

    @Override
    public String toString() {
        return String.format("SelectedRuleModel [ruleId=%s]", ruleId);
    }
}

The main elements of my view are a table form which shows each rule as a row. A radiobutton should populate the 'selectedRuleModel.ruleId' field with the value of the rule within the list.

<head>
<script type="text/javascript" src="<c:url value="/resources/js/jquery-2.1.1.min.js"/>"></script>
<script type="text/javascript" src="<c:url value="/resources/js/jquery.tablesorter.min.js"/>">  </script>
<script type="text/javascript" src="<c:url value="/resources/js/jquery.tablesorter.widgets.min.js"/>"></script>
<script type="text/javascript" src="<c:url value="/resources/js/rules.js"/>"></script>
<link href="<c:url value="/resources/css/base.css"/>" rel="stylesheet"/>
<link href="<c:url value="/resources/css/theme.blue.css"/>" rel="stylesheet"/>
<link href="<c:url value="/resources/css/rules.css"/>" rel="stylesheet"/>
</head>
<form:form method="POST" action="submit" modelAttribute="selectedRuleModel">
<button type="submit" name="delete" value="delete" class="btn btn-primary">Delete Selected Rule</button>
...
<c:forEach items="${listRulesModel.rules}" var="rule" varStatus="status">
<tr>
<td><form:radiobutton path="ruleId" value="${rule.id}"/></td>
<td>${rule.name}</td>
<td>${rule.batch}</td>
...
</form>

Everytime i submit the "delete row" form it appears that a new 'SelectedRuleModel' object is passed as a parameter to the deleteRule() method, such that the 'ruleId' value is always null. What am io doing wrong with my model/method mapping?

The generated HTML is

<form id="selectedRuleModel" action="submit" method="POST">
<button type="submit" name="amend" value="amend" class="btn btn-primary">Amend Selected Rule</button>
<button type="submit" name="branch" value="branch" class="btn btn-primary">Branch Selected Rule</button>
<button type="submit" name="delete" value="delete" class="btn btn-primary">Delete Selected Rule</button>
</div>

<!-- Add rule table -->
<table id="rulesTable" class="tablesorter">
<thead>
<tr>
<th>ID</th>
<th>Rule Name</th>
....
</tr>
</thead>
<tbody>
<tr>
<td><input id="ruleId2" name="ruleId" type="radio" value="20"/></td>
<td>Gender_Balance</td>
<td>*</td>
....

EDIT - I've update the controller method deleteRule() to include the BindResult object.

    @RequestMapping(value = "/submit", method = RequestMethod.POST, params = {"delete"})
public ModelAndView deleteRule(
        @Valid @ModelAttribute("selectedRuleModel") SelectedRuleModel selectedRuleModel,
        BindingResult result, ModelMap model) {
    System.out.println("deleteRule "+selectedRuleModel.getRuleId());
    System.out.println("model "+model.toString());
    System.out.println("BindingResult "+result.toString());
    if(selectedRuleModel.getRuleId()!=null)
        getRuleManager().deleteRule(getRuleManager().getRule(selectedRuleModel.getRuleId()));
    return new ModelAndView("redirect:/");
}

As you can see in the logging for this method, the 'selectedRuleModel' is null but the model has a 'selectedRuleModel' with a null 'ruleId' value.

2014-10-14 11:18:21,428 INFO  [STDOUT] (http-127.0.0.1-8080-2) deleteRule null
2014-10-14 11:18:21,428 INFO  [STDOUT] (http-127.0.0.1-8080-2) model {selectedRuleModel=SelectedRuleModel [ruleId=null], org.springframework.validation.BindingResult.selectedRuleModel=org.springframework.validation.BeanPropertyBindingResult: 0 errors}
2014-10-14 11:18:21,428 INFO  [STDOUT] (http-127.0.0.1-8080-2) BindingResult org.springframework.validation.BeanPropertyBindingResult: 0 errors

EDIT Adding web.xml and applicationContext.xml in case someone spots that i'm not initialising some required component correctly.

<web-app version="2.4"
         xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>

    <servlet>
        <servlet-name>hedgingcorrection</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>namespace</param-name>
            <param-value>applicationContext</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>hedgingcorrection</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <jsp-config>
        <taglib>
            <taglib-uri>/spring</taglib-uri>
            <taglib-location>/WEB-INF/tld/spring-form.tld</taglib-location>
        </taglib>
    </jsp-config>
</web-app>

And my applicationContext.xml has

<?xml version="1.0" encoding="UTF-8"?>
<beans
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <mvc:annotation-driven/>
    <mvc:resources mapping="/resources/**" location="/resources/"/>
    <mvc:view-controller path="/"/>

    <context:component-scan base-package="abc.xwz.web.correction.controller"/>

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

I removed the three mvc elements from my applicationContext.xml file it the form started working.

<mvc:annotation-driven/>
<mvc:resources mapping="/resources/**" location="/resources/"/>
<mvc:view-controller path="/"/>

FRIDAY EDIT - I've isolated the issue to the inclusion of the javascript elements in the jsp form. Without the javascript files included, the form submits data to the controller. When the scripts are added it seems that the form data is not correctly bound to the ModelAttribute object. The javascript files are primarily aimed at enabling the tablesorting features on the html table, so i can't understand why they are effecting the action of the submit buttons. 50 points for someone who can explain it.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The only thing I can think of is that rules.js or one of the other javascript imports may be disabling the radio button just before the submit - in which case no value would be submitted in the POST operation, which would then makes sense as to why the ModelAttribute is not being popualated.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...