1

As a backend programmer I rarely deal with the web side of things. So to say the least my grasp of the frameworks in general and Thymeleaf in particular is relatively simplistic. But now I have need to create a somewhat basic, but hopefully still decent web site to maintain a database. This is a very small schema and will be updated by internal users only. So it doesn't have to be customer facing polished nor hardened against all attackers. Still the programmer in me wants it to be more than just junk.

I think what would be nice is an html table representing database rows - DUH. But I'd like the entire table to be editable rather than making me code and the user click on individual rows to edit.

Ages ago I did something similar with JQuery. I also remember JQuery is best used by people that know what they are doing. So I am trying to accomplish this is with less complexity. I would like to still use Thymeleaf to at least make things a little interesting.

And this is where my knowledge ran aground. I cannot figure out how to access the data.

Since putting some limit on the size of the table seems like a good idea, my SpringMVC controller is returning a Page object containing the retrieved model objects. Thus my Response looks like this:

enter image description here

Shamelessly copying examples it was trivial to display the models in a table e.g.

  <tr th:each="restProvider : ${providerPage.content}">
  <td th:text="${restProvider.enabled}" />

This was all Great! But when I tried to make the table editable Thymeleaf wasn't so accomadating. The problem is I agree with it. In fact now it is hard to see why it worked before.

<form action="#" th:action="@{/saveProvider}" 
    th:object="${providerPage.content}" method="post">

<table border="1">
    <tbody>
    <tr th:each="restProvider, i : ${providerPage.content}" >
    <td th:style="'text-align: center;'" /> <input type="text" th:field="*{content[__${i.index}__].enabled}"  />

    <td th:text="${restProvider.serviceName}" />

org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/listProviders.html]")] with root cause org.springframework.beans.NotReadablePropertyException: Invalid property 'restProvider' of bean class [org.springframework.data.domain.PageImpl]: Bean property 'restProvider' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?

This all makes perfect sense. What I really need to do is cast PageImpl.content into an Array of RestProvider objects. But I don't have any idea how to do that.

My bests idea thus far is to either ditch Pagable entirely, which I don't want to do, or to put the Array of objects directly into the Model - thus sending 2 copies of the user data.

The first idea removes functionality - or at least makes me code it manually. As proposed here: type cast in EL expressions using Thymeleaf

The second is really just a variant on the first. I am pretty sure it would work. It also seems like a downright terrible idea and can't be the way the tool is supposed to be used.

EDIT 1

Using the advice of @Metroids I changed the field designation. But now I get

Bean property 'content.content[0]' is not readable or has an invalid getter method

So I did the seemingly logical thing and took out the "content". This left me with:

<td th:style="'text-align: center;'" /> <input type="text" th:field="*{[__${i.index}__].enabled}"  />

But that only got me as far as:

Bean property 'content.[0]' is not readable or has an invalid getter method

I've tried a few other things. Including messing around with how the iterator is specified. But none of them can get rid of the extra ".".

EDIT 2

Added the Form information

Terry
  • 911
  • 10
  • 26

1 Answers1

0

The problem here is that the expression *{restProvider.enabled} is trying to resolve to providerPage.restProvider.enabled which of course isn't a real thing. When you are using field expressions, you cannot use temporary variables created by th:each or th:with. You have build a complete expression that resolves to the forms th:object. Try this:

<tr th:each="restProvider, i : ${providerPage.content}">
    <td>
        <input type="text" th:field="*{content[__${i.index}__].enabled}"  />

This will resolved to an expression like providerPage.content[0].enabled.

Metroids
  • 18,999
  • 4
  • 41
  • 52
  • Thanks very much! This got me a lot further. But not quite there. Any ideas based on my update? – Terry Apr 08 '19 at 15:31
  • There really isn't much more to see. I added in the rest of the form, leaving out only the column headings. And all the other fields that haven't been converted to INPUTS yet. But this really is the whole deal. – Terry Apr 08 '19 at 15:46
  • 1
    You have `th:object="${providerPage.content}"` -- this is not a valid object. According to the docs: `Values for th:object attributes in form tags must be variable expressions (${...}) specifying only the name of a model attribute, without property navigation.` You have to use `th:object="${providerPage}"` like you had at the beginning and `th:field="*{content[__${i.index}__].enabled}"` as the field. – Metroids Apr 08 '19 at 19:05
  • Sorry. This is starting to make sense now. THANKS! – Terry Apr 08 '19 at 20:39