How to render Spring fields in a loop

classic Classic list List threaded Threaded
10 messages Options
Reply | Threaded
Open this post in threaded view
|

How to render Spring fields in a loop

NathanRussell
If I have a form backing object with a property which is a list of another simple type, something like this:

public class questionnaire() {
    private List<Person> person;

    public List<Person> getPerson() {
        return person;
    }

    public void setPerson(List<Person> person) {
        this.person = person;
    }
}

public class Person {
    private String name;
    // getters and setters
}

Then I have a thymeleaf template with:

<form th:object="questionnaire">
    ....
</form>

How can I loop through the Person list to render the name fields? I've tried the following and it doesn't work:

<div th:each="p : *{person}">
    <input th:field="*{p.name}" />
</div>

The error I get says that p is not a property of questionnaire, which of course it's not.

Any advice or pointers as to how I can produce this loop of fields would be very much appreciated,

Cheers

Nathan
Reply | Threaded
Open this post in threaded view
|

Re: How to render Spring fields in a loop

eiryu
try this

<form th:object="${questionnaire}">
Reply | Threaded
Open this post in threaded view
|

Re: How to render Spring fields in a loop

NathanRussell
Thanks for your suggestion of:

<form th:object="${questionnaire}">

But actually that's how I already have it (I forgot to type it that way in my original post!)

It feels to me that there either needs to be a way of changing the th:object within the context of the th:each loop, or there must be some other syntax for th:field within a th:each loop to refer to the current loop object instance.

Any comment from the thymeleaf dev team?

Cheers

Nathan
Reply | Threaded
Open this post in threaded view
|

Re: How to render Spring fields in a loop

Thibault Duchateau
Hi,

Did you try this ?

<div th:each="person : ${questionnaire.persons}">
<input th:text="${person.name}" />
</div>

Regards,
Thibault

Le 18 janv. 2013 à 20:55, "NathanRussell [via Thymeleaf - User Forum]" <[hidden email]> a écrit :

Thanks for your suggestion of:

<form th:object="${questionnaire}">

But actually that's how I already have it (I forgot to type it that way in my original post!)

It feels to me that there either needs to be a way of changing the th:object within the context of the th:each loop, or there must be some other syntax for th:field within a th:each loop to refer to the current loop object instance.

Any comment from the thymeleaf dev team?

Cheers

Nathan


If you reply to this email, your message will be added to the discussion below:
http://forum.thymeleaf.org/How-to-render-Spring-fields-in-a-loop-tp4025542p4025544.html
To start a new topic under General Usage, email [hidden email]
To unsubscribe from General Usage, click here.
NAML
Reply | Threaded
Open this post in threaded view
|

Re: How to render Spring fields in a loop

NathanRussell
Yes, I've kind of tried that. th:text is used to put content in an element. So, something like:

<div th:text="${person.name}" />

Would produce:
<div>Nathan</div>

Doing as you've suggested with an input field would produce:
<input>Nathan</input>
Which is not valid HTML, and is not how to mark up an input field.

The th:field attribute marks up a HTML form field element (such as input, select) with the correctly formed name attribute referring to the form backing object property, and a correct value attribute. IE. it should produce:

<input name="person[2].name" value="Nathan" />
(Assuming it was the 3rd instance in the list)

There must be a way of marking up input fields in a loop ... Surely ... ???
Nim
Reply | Threaded
Open this post in threaded view
|

Re: How to render Spring fields in a loop

Nim
Use th:value="${person.name}"

This should fill the text input with the person's name, if that's what you want.
Reply | Threaded
Open this post in threaded view
|

Re: How to render Spring fields in a loop

Thibault Duchateau
My mistake, Nim, +1 :-)

Le 18 janv. 2013 à 21:48, "Nim [via Thymeleaf - User Forum]" <[hidden email]> a écrit :

use th:value=${person.name}


If you reply to this email, your message will be added to the discussion below:
http://forum.thymeleaf.org/How-to-render-Spring-fields-in-a-loop-tp4025542p4025548.html
To start a new topic under General Usage, email [hidden email]
To unsubscribe from General Usage, click here.
NAML
Reply | Threaded
Open this post in threaded view
|

Re: How to render Spring fields in a loop

danielfernandez
Administrator
In reply to this post by NathanRussell
NathanRussell wrote
How can I loop through the Person list to render the name fields? I've tried the following and it doesn't work:

<div th:each="p : *{person}">
    <input th:field="*{p.name}" />
</div>

Oh, this is a good one :-)

Actually, Thymeleaf allows you bind your form input to a specific position in your list of Person objects, so you could do:

    <input th:field="*{person[0].name}" />
    <input th:field="*{person[1].name}" />
    <input th:field="*{person[2].name}" />

...but this would not be very adequate, because you'd have to know the number of positions in "person" list beforehand. So you could say: "OK, let's iterate an index from 0 to p's length", something you can do using Thymeleaf's "#numbers.sequence(...)" utility method:

    <div th:each="index : ${#numbers.sequence(0,(#object.person.size() - 1))}">
        <input th:field="*{person[index].name}" />
    </div>

But ooops! this doesn't work. Why? Because Spring EL does not allow the use of variables inside indexed arguments, so that "person[index]" part is not valid Spring EL. Only constants (numeric or literal) are allowed.

How can you solve this in Thymeleaf? Using expression pre-processing, so that you "index" variable is resolved before (at a "first pass") and the Spring EL engine receives a numeric literal when it executes the whole expression:

    <div th:each="index : ${#numbers.sequence(0,(#object.person.size() - 1))}">
        <input th:field="*{person[__${index}__].name}" />
    </div>


Last, but not least... note you wouldn't have been able to do at all this using JSP ;-)

Regards,
Daniel.
Reply | Threaded
Open this post in threaded view
|

Re: How to render Spring fields in a loop

NathanRussell
Thanks Daniel for your reply, and thank you also for understanding the original question/problem :)

That looks like a great solution, though a little convoluted/geeky :)
Out of interest ... is this type of thing covered in the PDF documentation? Would I / should I have been able to work this out for myself without asking on the forum?

Thanks again,

Nathan

(PS. You could absolutely have done this in JSP)
Reply | Threaded
Open this post in threaded view
|

Re: How to render Spring fields in a loop

danielfernandez
Administrator
NathanRussell wrote
That looks like a great solution, though a little convoluted/geeky :)
Actually, a bit after writing my response I realised the "#numbers.sequence(...)" part was in fact uneeded, because we could simulate it just using the iteration status variable (which is automatically declared adding 'Stat' to the iteration variable):


    <div th:each="p : *{person}">
        <input th:field="*{person[__${pStat.index}__].name}" />
    </div>


NathanRussell wrote
Out of interest ... is this type of thing covered in the PDF documentation? Would I / should I have been able to work this out for myself without asking on the forum?
Expression preprocessing is explained in page 26 of the "Using Thymeleaf" tutorial, and this specific scenario of using it for accessing indexed form properties in Spring EL is explained at the end of page 18 of the "Thymeleaf + Spring 3" tutorial.

But it is not at all a problem with an intuitive solution, so it's perfectly fine to ask it at the forum :-)

NathanRussell wrote
(PS. You could absolutely have done this in JSP)
I don't think so ;-). I mean, of course JSP is quite low-level and you can create some scriplets or an amount of complex JSTL code in a JSP page to do this, so in the end you could come up with an HTML-valid solution... but it would not be a Spring-backed solution, because you would not be using Spring's JSP tag libraries for your inputs.

You are not able to bind a List<?> Spring form input like this using Spring's JSP support (its JSP tag libraries) because no Spring JSP tag allows anything like "expresion preprocessing", that's a Thymeleaf feature. And given the fact that Spring EL does not allow using variables for indexed properties, you would not be able to create this kind of <input> inside an iteration. You would have to know the amount of list elements beforehand... or not use Spring tags.

In fact, this specific scenario of indexed properties in Spring forms was the reason I created the expression preprocessing mechanism in Thymeleaf.


Regards,
Daniel.