Java Thymeleaf中嵌套列表的表单绑定

Java Thymeleaf中嵌套列表的表单绑定,java,spring,spring-mvc,thymeleaf,Java,Spring,Spring Mvc,Thymeleaf,我正在使用SpringBoot、SpringMVC和thymeleaf开发一个web应用程序。我想创建一个简单的表单页面,用户可以编辑配置属性并保存更改。我已经阅读了这里的相关问题,但仍然感到困惑。当我试图呈现页面时,我得到了非法状态异常,无法解决问题 例外情况: org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path res

我正在使用SpringBoot、SpringMVC和thymeleaf开发一个web应用程序。我想创建一个简单的表单页面,用户可以编辑配置属性并保存更改。我已经阅读了这里的相关问题,但仍然感到困惑。当我试图呈现页面时,我得到了非法状态异常,无法解决问题

例外情况:

org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/test.html]")
    at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:241) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
    ..........
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_131]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.31.jar:8.5.31]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_131]
Caused by: org.attoparser.ParseException: Error during execution of processor 'org.thymeleaf.spring5.processor.SpringInputGeneralFieldTagProcessor' (template: "test" - line 11, col 36)
    at org.attoparser.MarkupParser.parseDocument(MarkupParser.java:393) ~[attoparser-2.0.4.RELEASE.jar:2.0.4.RELEASE]
    at org.attoparser.MarkupParser.parse(MarkupParser.java:257) ~[attoparser-2.0.4.RELEASE.jar:2.0.4.RELEASE]
    at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:230) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
    ... 58 common frames omitted
Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Error during execution of processor 'org.thymeleaf.spring5.processor.SpringInputGeneralFieldTagProcessor' (template: "test" - line 11, col 36)
    at org.thymeleaf.processor.element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:117) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
    at org.thymeleaf.processor.element.AbstractElementTagProcessor.process(AbstractElementTagProcessor.java:95) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
    ..........
    at org.thymeleaf.spring5.processor.AbstractSpringFieldTagProcessor.doProcess(AbstractSpringFieldTagProcessor.java:174) ~[thymeleaf-spring5-3.0.9.RELEASE.jar:3.0.9.RELEASE]
    at org.thymeleaf.processor.element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:74) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
    ... 85 common frames omitted

2018-07-27 14:39:47.029 ERROR 2168 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/test.html]")] with root cause

java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'props[0]' available as request attribute
    at org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:153) ~[spring-webmvc-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.web.servlet.support.RequestContext.getBindStatus(RequestContext.java:903) ~[spring-webmvc-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    ..........
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_131]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.31.jar:8.5.31]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_131]
public class FlumeConfiguration {
    public String flumeName = "Flume Name";
    public List<SinkConfiguration> sinkConfigurationList = new ArrayList<SinkConfiguration>();

    public FlumeConfiguration(){
    }

    public String getFlumeName() {
        return flumeName;
    }

    public void setFlumeName(String flumeName) {
        this.flumeName = flumeName;
    }

    public List<SinkConfiguration> getSinkConfigurationList() {
        return sinkConfigurationList;
    }

    public void setSinkConfigurationList(List<SinkConfiguration> sinkConfigurationList) {
        this.sinkConfigurationList = sinkConfigurationList;
    }
}
public class SinkConfiguration {
    public String port;
    public List<ConfItem> props = new ArrayList<ConfItem>();

    public SinkConfiguration(){
        port = "123";
    }

    public String getPort() {
        return port;
    }

    public void setPort(String port) {
        this.port = port;
    }

    public List<ConfItem> getProps() {
        return props;
    }

    public void setProps(List<ConfItem> props) {
        this.props = props;
    }
}
public class ConfItem {
    public String key;
    public String value;
    public ConfItem(){

    }

    public ConfItem(String key, String value) {
        this.key = key;
        this.value = value;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}
@Controller
public class TestController {
    @RequestMapping(value = "/test", method = RequestMethod.GET)
    public ModelAndView test(ModelAndView mav) {
        FlumeConfiguration conf = new FlumeConfiguration();
        SinkConfiguration sinkConfiguration = new SinkConfiguration();
        sinkConfiguration.props.add(new ConfItem("port", "211"));
        sinkConfiguration.props.add(new ConfItem("ip", "1.1.1.1"));
        conf.sinkConfigurationList.add(sinkConfiguration);

        mav.setViewName("test");
        mav.addObject("conf", conf);
        return mav;
    }

    @RequestMapping(value = "/test", method = RequestMethod.POST)
    public String test(@ModelAttribute FlumeConfiguration conf,
                       BindingResult result, ModelMap model) {

        return "ok";
    }
}
TestController类:

org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/test.html]")
    at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:241) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
    ..........
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_131]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.31.jar:8.5.31]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_131]
Caused by: org.attoparser.ParseException: Error during execution of processor 'org.thymeleaf.spring5.processor.SpringInputGeneralFieldTagProcessor' (template: "test" - line 11, col 36)
    at org.attoparser.MarkupParser.parseDocument(MarkupParser.java:393) ~[attoparser-2.0.4.RELEASE.jar:2.0.4.RELEASE]
    at org.attoparser.MarkupParser.parse(MarkupParser.java:257) ~[attoparser-2.0.4.RELEASE.jar:2.0.4.RELEASE]
    at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:230) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
    ... 58 common frames omitted
Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Error during execution of processor 'org.thymeleaf.spring5.processor.SpringInputGeneralFieldTagProcessor' (template: "test" - line 11, col 36)
    at org.thymeleaf.processor.element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:117) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
    at org.thymeleaf.processor.element.AbstractElementTagProcessor.process(AbstractElementTagProcessor.java:95) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
    ..........
    at org.thymeleaf.spring5.processor.AbstractSpringFieldTagProcessor.doProcess(AbstractSpringFieldTagProcessor.java:174) ~[thymeleaf-spring5-3.0.9.RELEASE.jar:3.0.9.RELEASE]
    at org.thymeleaf.processor.element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:74) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
    ... 85 common frames omitted

2018-07-27 14:39:47.029 ERROR 2168 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/test.html]")] with root cause

java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'props[0]' available as request attribute
    at org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:153) ~[spring-webmvc-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.web.servlet.support.RequestContext.getBindStatus(RequestContext.java:903) ~[spring-webmvc-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    ..........
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_131]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.31.jar:8.5.31]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_131]
public class FlumeConfiguration {
    public String flumeName = "Flume Name";
    public List<SinkConfiguration> sinkConfigurationList = new ArrayList<SinkConfiguration>();

    public FlumeConfiguration(){
    }

    public String getFlumeName() {
        return flumeName;
    }

    public void setFlumeName(String flumeName) {
        this.flumeName = flumeName;
    }

    public List<SinkConfiguration> getSinkConfigurationList() {
        return sinkConfigurationList;
    }

    public void setSinkConfigurationList(List<SinkConfiguration> sinkConfigurationList) {
        this.sinkConfigurationList = sinkConfigurationList;
    }
}
public class SinkConfiguration {
    public String port;
    public List<ConfItem> props = new ArrayList<ConfItem>();

    public SinkConfiguration(){
        port = "123";
    }

    public String getPort() {
        return port;
    }

    public void setPort(String port) {
        this.port = port;
    }

    public List<ConfItem> getProps() {
        return props;
    }

    public void setProps(List<ConfItem> props) {
        this.props = props;
    }
}
public class ConfItem {
    public String key;
    public String value;
    public ConfItem(){

    }

    public ConfItem(String key, String value) {
        this.key = key;
        this.value = value;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}
@Controller
public class TestController {
    @RequestMapping(value = "/test", method = RequestMethod.GET)
    public ModelAndView test(ModelAndView mav) {
        FlumeConfiguration conf = new FlumeConfiguration();
        SinkConfiguration sinkConfiguration = new SinkConfiguration();
        sinkConfiguration.props.add(new ConfItem("port", "211"));
        sinkConfiguration.props.add(new ConfItem("ip", "1.1.1.1"));
        conf.sinkConfigurationList.add(sinkConfiguration);

        mav.setViewName("test");
        mav.addObject("conf", conf);
        return mav;
    }

    @RequestMapping(value = "/test", method = RequestMethod.POST)
    public String test(@ModelAttribute FlumeConfiguration conf,
                       BindingResult result, ModelMap model) {

        return "ok";
    }
}
Test.html

<!DOCTYPE html>
<html lang="en" xmlns:layout="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="#" th:action="@{/test}" method="post" >
        <th:block th:each="sink, sinkStat : ${conf.sinkConfigurationList}">
            <th:block th:each="pr, prStat : ${sink.props}">
                <input type="text" th:field="*{props[__${prStat.index}__].key}" th:value="${pr.key}">
                <input type="text" th:field="*{props[__${prStat.index}__].value}" th:value="${pr.value}">
            </th:block>
        </th:block>
        <input type="submit" class="button-primary float-none" />
    </form>
</body>
</html>

标题

我希望这是足够的信息-如果需要提供更多信息,请告诉我。

您需要在th:field或th:value+th:name+th:id之间做出选择。您不能同时生成两个th:fieldthem@roberttrudel非常感谢。删除th:value和th:name,但异常仍然存在。您是否使其正常工作?如果你现在有一个有效的例子,并且你可以自己回答,这将非常有帮助