Amadeu Cabanilles
Amadeu Cabanilles

Reputation: 963

Spring Boot and form Thymeleaf. Accessing object properties from the template

I am currently trying out with Spring Boot, and have developed some smaller CRUD-like applications with Spring Boot and Thymeleaf.

Here my controller

@Controller
public class Home {

    @RequestMapping(value="/", method=RequestMethod.GET)
    public String index(Post post) {
        return "index";
    }

    @RequestMapping(value = "/", method = RequestMethod.POST)
    public String addNewPost(@Valid Post post, BindingResult bindingResult, Model model) {
        if (bindingResult.hasErrors()) {
            return "index";
        }
        model.addAttribute("title",   post.getTitle());
        model.addAttribute("content", post.getContent());
        return "result";
    }
}

My index.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Spring Boot and Thymeleaf example</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
    <h3>Spring Boot and Thymeleaf, part 2 - forms</h3>
    <form action="#" th:action="@{/}" th:object="${post}" method="post">
        <table>
            <tr>
                <td>Title:</td>
                <td><input type="text" th:field="*{post}" /></td>
                <td th:if="${#fields.hasErrors('title')}" th:errors="*{title}">Title error message</td>
            </tr>
            <tr>
                <td>Content:</td>
                <td><input type="text" th:field="*{post}" /></td>
                <td th:if="${#fields.hasErrors('content')}" th:errors="*{content}">Content error message</td>
            </tr>
            <tr>
                <td><button type="submit">Submit post</button></td>
            </tr>
        </table>
    </form>
</body>
</html>

And I wonder if instead of adding every property of the object Post one by one I could add the object itself.

I've tried this

@Controller
public class Home {

    @RequestMapping(value="/", method=RequestMethod.GET)
    public String index(Post post) {
        return "index";
    }

    @RequestMapping(value = "/", method = RequestMethod.POST)
    public String addNewPost(@Valid Post post, BindingResult bindingResult, Model model) {
        if (bindingResult.hasErrors()) {
            return "index";
        }
        model.addAttribute("post", post);       
        return "result";
    }
}

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Spring Boot and Thymeleaf example</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
    <h3>Spring Boot and Thymeleaf, part 2 - forms</h3>
    <form action="#" th:action="@{/}" th:object="${post}" method="post">
        <table>
            <tr>
                <td>Title:</td>
                <td><input type="text" th:field="*{title}" /></td>
                <td th:if="${#fields.hasErrors('title')}" th:errors="*{title}">Title error message</td>
            </tr>
            <tr>
                <td>Content:</td>
                <td><input type="text" th:field="*{content}" /></td>
                <td th:if="${#fields.hasErrors('content')}" th:errors="*{content}">Content error message</td>
            </tr>
            <tr>
                <td><button type="submit">Submit post</button></td>
            </tr>
        </table>
    </form>
</body>
</html>

But then I got this error:

org.springframework.beans.NotReadablePropertyException: Invalid property 'post' of bean class [com.jvmhub.springboot.form.Post]: Bean property 'post' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
    at org.springframework.beans.BeanWrapperImpl.getPropertyValue(BeanWrapperImpl.java:731)
    at org.springframework.beans.BeanWrapperImpl.getNestedBeanWrapper(BeanWrapperImpl.java:572)
    at org.springframework.beans.BeanWrapperImpl.getBeanWrapperForPropertyPath(BeanWrapperImpl.java:549)
    at org.springframework.beans.BeanWrapperImpl.getPropertyValue(BeanWrapperImpl.java:720)
    at org.springframework.validation.AbstractPropertyBindingResult.getActualFieldValue(AbstractPropertyBindingResult.java:99)
    at org.springframework.validation.AbstractBindingResult.getFieldValue(AbstractBindingResult.java:229)
    at org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:120)
    at org.thymeleaf.spring4.util.FieldUtils.getBindStatusFromParsedExpression(FieldUtils.java:396)
    at org.thymeleaf.spring4.util.FieldUtils.getBindStatus(FieldUtils.java:323)
    at org.thymeleaf.spring4.util.FieldUtils.getBindStatus(FieldUtils.java:289)
    at org.thymeleaf.spring4.processor.attr.AbstractSpringFieldAttrProcessor.processAttribute(AbstractSpringFieldAttrProcessor.java:98)
    at org.thymeleaf.processor.attr.AbstractAttrProcessor.doProcess(AbstractAttrProcessor.java:87)
    at org.thymeleaf.processor.AbstractProcessor.process(AbstractProcessor.java:212)
    at org.thymeleaf.dom.Node.applyNextProcessor(Node.java:1017)
    at org.thymeleaf.dom.Node.processNode(Node.java:972)
    at org.thymeleaf.dom.NestableNode.computeNextChild(NestableNode.java:695)
    at org.thymeleaf.dom.NestableNode.doAdditionalProcess(NestableNode.java:668)
    at org.thymeleaf.dom.Node.processNode(Node.java:990)
    at org.thymeleaf.dom.NestableNode.computeNextChild(NestableNode.java:695)
    at org.thymeleaf.dom.NestableNode.doAdditionalProcess(NestableNode.java:668)
    at org.thymeleaf.dom.Node.processNode(Node.java:990)
    at org.thymeleaf.dom.NestableNode.computeNextChild(NestableNode.java:695)
    at org.thymeleaf.dom.NestableNode.doAdditionalProcess(NestableNode.java:668)
    at org.thymeleaf.dom.Node.processNode(Node.java:990)
    at org.thymeleaf.dom.NestableNode.computeNextChild(NestableNode.java:695)
    at org.thymeleaf.dom.NestableNode.doAdditionalProcess(NestableNode.java:668)
    at org.thymeleaf.dom.Node.processNode(Node.java:990)
    at org.thymeleaf.dom.NestableNode.computeNextChild(NestableNode.java:695)
    at org.thymeleaf.dom.NestableNode.doAdditionalProcess(NestableNode.java:668)
    at org.thymeleaf.dom.Node.processNode(Node.java:990)
    at org.thymeleaf.dom.NestableNode.computeNextChild(NestableNode.java:695)
    at org.thymeleaf.dom.NestableNode.doAdditionalProcess(NestableNode.java:668)
    at org.thymeleaf.dom.Node.processNode(Node.java:990)
    at org.thymeleaf.dom.NestableNode.computeNextChild(NestableNode.java:695)
    at org.thymeleaf.dom.NestableNode.doAdditionalProcess(NestableNode.java:668)
    at org.thymeleaf.dom.Node.processNode(Node.java:990)
    at org.thymeleaf.dom.Document.process(Document.java:93)
    at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1155)
    at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1060)
    at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1011)
    at org.thymeleaf.spring4.view.ThymeleafView.renderFragment(ThymeleafView.java:335)
    at org.thymeleaf.spring4.view.ThymeleafView.render(ThymeleafView.java:190)
    at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1244)
    at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1027)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:971)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:858)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:85)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:668)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1521)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1478)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)

Upvotes: 1

Views: 6309

Answers (2)

Saket Yadav
Saket Yadav

Reputation: 1017

To access object property , kindly use.

 <h1 th:text="${title}" ></h1>

Upvotes: 2

Branislav Lazic
Branislav Lazic

Reputation: 14806

Yes. Just add post object as a model attribute:

model.addAttribute("post", post);

To access its properties use simply:

<td><input type="text" th:text="${post.title}" /></td>

P.S. Your bindings in form are incorrect. If you have an th:object="${post}", you access its properties:

<td><input type="text" th:field="*{title}" /></td>

Upvotes: 1

Related Questions