2

I can see List being used successfully in Thymeleaf forms in http://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html#dynamic-fields

<tr th:each="row,rowStat : *{rows}">
  <td th:text="${rowStat.count}">1</td>
  <td>
    <select th:field="*{rows[__${rowStat.index}__].variety}"><!-- how to do this with Set? -->
      <option th:each="var : ${allVarieties}" 
              th:value="${var.id}" 
              th:text="${var.name}">Thymus Thymi</option>
    </select>
  </td>
  ..
</tr>

Can I do the same with Set instead of List? Specifically the line with comment in code. How do I assign Set elements to th:field?

Update 1

I used this in my project and proceeded to use Set and on submit/add new row, it gives below error. It does not even reach corresponding controller method.

org.springframework.beans.InvalidPropertyException: Invalid property 'catDescrDts[0]' of bean class [ae.tbits.atn.aiwacore.common.model.CatDescr]: Illegal attempt to get property 'catDescrDts' threw exception; nested exception is org.springframework.beans.InvalidPropertyException: Invalid property 'catDescrDts[0]' of bean class [ae.tbits.atn.aiwacore.common.model.CatDescr]: Cannot get element with index 0 from Set of size 0, accessed using property path 'catDescrDts[0]'

CatDescrDts correspond to the rows in the example, it has many to one relation with CatDescr entity.

Stack trace

Caused by: org.springframework.beans.InvalidPropertyException: Invalid property 'catDescrDts[0]' of bean class [ae.tbits.atn.aiwacore.common.model.CatDescr]: Cannot get element with index 0 from Set of size 0, accessed using property path 'catDescrDts[0]'
at org.springframework.beans.AbstractNestablePropertyAccessor.getPropertyValue(AbstractNestablePropertyAccessor.java:661)
... 51 more
2017-07-13 16:23:57 ERROR ErrorController:19 - Exception during execution of SpringSecurity application
org.springframework.beans.InvalidPropertyException: Invalid property 'catDescrDts[0]' of bean class [ae.tbits.atn.aiwacore.common.model.CatDescr]: Illegal attempt to get property 'catDescrDts' threw exception; nested exception is org.springframework.beans.InvalidPropertyException: Invalid property 'catDescrDts[0]' of bean class [ae.tbits.atn.aiwacore.common.model.CatDescr]: Cannot get element with index 0 from Set of size 0, accessed using property path 'catDescrDts[0]'
at org.springframework.beans.AbstractNestablePropertyAccessor.getPropertyValue(AbstractNestablePropertyAccessor.java:708)
at org.springframework.beans.AbstractNestablePropertyAccessor.getNestedPropertyAccessor(AbstractNestablePropertyAccessor.java:836)
at org.springframework.beans.AbstractNestablePropertyAccessor.getPropertyAccessorForPropertyPath(AbstractNestablePropertyAccessor.java:813)
at org.springframework.beans.AbstractNestablePropertyAccessor.setPropertyValue(AbstractNestablePropertyAccessor.java:270)
at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:95)
at org.springframework.validation.DataBinder.applyPropertyValues(DataBinder.java:834)
at org.springframework.validation.DataBinder.doBind(DataBinder.java:730)
at org.springframework.web.bind.WebDataBinder.doBind(WebDataBinder.java:192)
at org.springframework.web.bind.ServletRequestDataBinder.bind(ServletRequestDataBinder.java:106)
at org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor.bindRequestParameters(ServletModelAttributeMethodProcessor.java:150)
at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:114)
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:161)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:128)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:114)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:707)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85)
at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
at org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:131)
at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
at io.undertow.security.handlers.NotificationReceiverHandler.handleRequest(NotificationReceiverHandler.java:50)
at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:284)
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:263)
at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81)
at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:174)
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:202)
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:793)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)

Caused by: org.springframework.beans.InvalidPropertyException: Invalid property 'catDescrDts[0]' of bean class [ae.tbits.atn.aiwacore.common.model.CatDescr]: Cannot get element with index 0 from Set of size 0, accessed using property path 'catDescrDts[0]'
at org.springframework.beans.AbstractNestablePropertyAccessor.getPropertyValue(AbstractNestablePropertyAccessor.java:661)
... 51 more

Update 2

Putting some code..

  1. When create is first called, form shows up fine with empty row.
  2. When I click on save or add new row, that error throws up before controller save() is called.
  3. If I comment out th:field line, error is gone.

Controller

@GetMapping({"/create"})
public String create(ModelMap model){
    CatDescr catDescr = new CatDescr();         
    //add 1 empty row for catDescrDt
    int noOfCatDescrDts = 1;
    catDescr.setCatDescrDts(getNewCatDescrDts(noOfCatDescrDts, catDescr));  
    model.addAttribute("catDescr", catDescr);   
    return "catDescr/create";
}

@PostMapping(value="/create", params={"save"})
public String save(final CatDescr catDescr
        , final BindingResult bindingResult, final ModelMap model){     
    ...
    CatDescr updatedCatDescr = catDescrService.save(catDescr);  
    model.clear();
    return "redirect:/catDescr/create";

}

@RequestMapping(value="/create", params={"addCatDescrDtRow"})
public String addCatDescrDtRow(ModelMap model, final CatDescr catDescr, final BindingResult bindingResult) {
    catDescr.getCatDescrDts().add(new CatDescrDt());
    ...
    return "catDescr/create";
}

View

<ul th:each="item, stat: *{catDescrDts}">
<li>
    <input type="text" th:field="*{catDescrDts[__${stat.index}__].name}" th:id="${'catDescrDts'+stat.index+'.name'}">
</li>

Philip YW
  • 150
  • 1
  • 2
  • 15
  • Yes, it should work fine with Set as well in fact any Iterable or Array should be fine. Though you may not be able to guarantee the order for the Set elements. – Ranjeet Jul 13 '17 at 11:27
  • Tried, doesn't work. Error seems to be at the line where set being accessed like an array. Posted stack trace.. Thanks. – Philip YW Jul 13 '17 at 13:28
  • the exception seems to say that you are trying to access the first element from the set, but the set is empty. – Janar Jul 13 '17 at 16:27
  • @Janar Yes you are right. Except nowhere in my code i try to access Set's element directly eg [0]. Checked my controller, set is not empty. And view shows up the form initially, when I post (either save or add new row) I get this error.Stack trace seems to indicate error on post, not reaching controller. Will put code snippets in Update 2. – Philip YW Jul 13 '17 at 17:21
  • 2
    Sorry I think I misinterpreted your question. Maybe I should clarify You can use the counters and indices for Set fine in Thymeleaf but you cannot bind a Set back in your controller to an object directly. Spring uses getters and setters to bind the object which it cant by using indices with a Set. Try Custom Getters/Setters like [this](https://stackoverflow.com/a/28505620/8068435) – Ranjeet Jul 13 '17 at 17:49
  • @Dark You were bang on. That error is gone now, your link was helpful. Thanks!! – Philip YW Jul 13 '17 at 18:42

1 Answers1

1

Had to use custom getter/setter in entity to return Set as List, bind the List in Thymeleaf instead so it can be accessed by index.

In controller had to sync the List member with Set member.

All credit to @Dark, wonderful explanation as well as useful link.

Philip YW
  • 150
  • 1
  • 2
  • 15