3

On a specific view I do have an expandable list of employees. Each employee has 2 fields and those are hoursWorked & advancePayments. With 1 button click, I would like to post the whole form. To achieve this, I'm sending a list to the view that contains several POJOs (based on number of employees).

A POJOs that are added to the list look like this:

@Setter
@Getter
@NoArgsConstructor
public class WorkdayCommand {

    private Long employeeId;
    private Integer hoursWorked;
    private Integer advancePayment;
}

In a controller, I do have a line to add the list to the model:

model.addAttribute("workdayCommands", employeeService.getListOfWorkdayCommandsWithIds());

And the method forming the actual list:

public List<WorkdayCommand> getListOfWorkdayCommandsWithIds(){

    List<WorkdayCommand> listOfCommands = new ArrayList<>();
    List<Long> listOfIds = employeeRepository.getListOfIds();

    for(int i = 0; i < employeeRepository.getNumberOfEmployees(); i++){

        WorkdayCommand workdayCommand = new WorkdayCommand();
        workdayCommand.setEmployeeId(listOfIds.get(i));
        listOfCommands.add(workdayCommand);
    }

    return listOfCommands;
}

Now, I am having a problem with my view:

<div class="table-responsive" th:if="${not #lists.isEmpty(employees)}">
    <form th:object="${workdayCommands}" th:action="@{/addworkday}">
         some table headers...
         <tr th:each="employee : ${employees}">
              <td><a href="#" role="button" th:href="@{'/employee/' + ${employee.id}}" th:text="${employee.name}">Mike Kowalsky</a></td>
              <td><input type="text" class="form-control" placeholder="Enter hours" th:field="*{hoursWorked}"></td>
              <td><input type="text" class="form-control" placeholder="Enter payment" th:field="*{advancePayment}"></td>
              <td><input type="hidden" th:field="*{id}"/></td>
         </tr>
    </form>
</div>

So far, I keep getting an error:

NotReadablePropertyException: Invalid property 'hoursWorked' of bean class [java.util.ArrayList]: Bean property 'hoursWorked' is not readable or has an invalid getter method

How to properly bind the arraylist with the view? I guess the problem is that there's no such field in the arraylist as hoursWorked. What th:field parameter should I use, to get to the actual WorkdayCommand.hoursWorked field and then iterate through the list to get through all the employees? If you need any more information, feel free to ask any time.

I'm trying with things like:

th:field="*{[__${iter.index}__].hoursWorked}"

...but that still doesn't work. I can't relate to the first POJO on the list.

edit 2

My full view looks like this: enter image description here

In a single table row I do have some employee information as well as 2 inputs and 2 buttons. Each row is created thanks to:

 <tr th:each="employee : ${employees}">

When a submit button is hit, a new Workday object is created and then persisted to the database. When this happens, a Workday needs to be associated to a corresponding employee. So with my:

 <tr th:each="employee : ${employees}">

...I'm also assigning a hidden id field. Then we have the WorkdayCommand that gathers all the information from the view. So the employeeId field is my way of associating a Workday to a corresponding employee. It should use the id value that was passed by for each that displayed all the information. Hope it's clear now.

2
  • You should also iterate on workdayCommands, just as on employees Commented Mar 13, 2018 at 14:20
  • Any leads on where do I do that? In the form tag? Commented Mar 13, 2018 at 14:26

1 Answer 1

5

You can't bind directly to an ArrayList aS the th:object. Instead you should create an object that has the ArrayList as a property.

@Setter
@Getter
@NoArgsConstructor
public class WorkdayForm {
    private List<WorkdayCommand> workdayCommands = new ArrayList<>();
}

Then you loop through, and bind them like this:

<form th:object="${workdayForm}" th:action="@{/addworkday}">
  <!-- table headers -->
  <tr th:each="workdayCommand, i : ${workdayForm.workdayCommand}">
      <td><input type="text" class="form-control" placeholder="Enter hours" th:field="*{workdayCommands[__${i.index}__].hoursWorked}"></td>
      <td><input type="text" class="form-control" placeholder="Enter payment" th:field="*{workdayCommands[__${i.index}__].advancePayment}"></td>
      <td><input type="hidden" th:field="*{workdayCommands[__${i.index}__].employeeId}"/></td>
  </tr>
  <!-- table footers -->
</form>
Sign up to request clarification or add additional context in comments.

4 Comments

Alright. That's reasonable. There's 1 more problem tho. First, when iterating through employees, the hidden field id gets value assigned. The same value needs to be passed to the employeeId field in the backing object. That way, I'll be sure, the hours and payments are assigned to the correct employee. I can't include 2 th:each in a single table row... Any clues on how to resolve that?
I'm not sure what you mean... what is employeeId on the workCommand? Why do you need to loop over employees?
workdayCommand should have an employee as a property of it then... as part of the loop, you could display employee information like this: <td><a href="#" role="button" th:href="@{'/employee/' + ${employee.id}}" th:text="${workdayCommand.employee.name}">Mike Kowalsky</a></td>
Alternatively you could just loop over employee instead of workdayCommand. Everything would stay the same, as far as I can see.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.