简体   繁体   中英

Input in array Thymeleaf

I need choose values from one array and assign it to other array. Using Spring Thymeleaf. No idea how retrieve these choosed values. My classes:

@Entity
public class Collaborator {
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Long id;

   @NotNull
   @Size (min=3, max=32)
   private String name;

   @NotNull
   @ManyToOne (cascade = CascadeType.ALL)
   private Role role;

   public Collaborator() {}...

@Entity
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @Size(min = 3, max = 99)
    private String name;

    public Role() {}....

My controllers:

@RequestMapping("/project_collaborators/{projectId}")
public String projectCollaborators(@PathVariable Long projectId, Model model) {
    Project project = mProjectService.findById(projectId);
    List<Collaborator> allCollaborators = mCollaboratorService.findAll();
    List<Collaborator> assignments = new ArrayList<>();

    if (project.getRolesNeeded()!=null) {
        for (int i=0;i<project.getRolesNeeded().size();i++) {
            assignments.add(new Collaborator("Unassigned", project.getRolesNeeded().get(i)));
            assignments.get(i).setId((long) 0);
        }
    }

    model.addAttribute("assignments", assignments);
    model.addAttribute("allCollaborators", allCollaborators);
    model.addAttribute("project", project);

    return "project_collaborators";
}

@RequestMapping(value = "/project_collaborators/{projectId}", method = RequestMethod.POST)
public String projectCollaboratorsPost(@ModelAttribute Project project, @PathVariable Long projectId, Model model) {
    Project p = mProjectService.findById(projectId);


    //mProjectService.save(project);
    return "redirect:/project_detail/{projectId}";
}

And template:

            <form th:action="@{'/project_collaborators/' + ${project.id}}" method="post" th:object="${project}">
                <label th:text="'Edit Collaborators: ' + ${project.name}">Edit Collaborators: Website Project</label>
                <ul class="checkbox-list">

                    <li th:each="a : ${assignments}">
                        <span th:text="${a.role.name}" class="primary">Developer</span>
                        <div class="custom-select">
                            <span class="dropdown-arrow"></span>
                            <select th:field="${a.id}">
                                <option th:each="collaborator : ${allCollaborators}" th:value="${collaborator.id}" th:text="${collaborator.name}">Michael Pemulis</option>
                            </select>
                        </div>
                    </li>

                </ul>
                <div class="actions">
                    <input type="submit" value="Save" class="button"/>
                    <a href="#" class="button button-secondary">Cancel</a>
                </div>
            </form>

As you can see I want to let user choose for each role (roleNeeded) any collaborator from (allCollaborators) and keep that assigns in List (assignments).

And I get error message:

ava.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'a' available as request attribute

So question is: how to solve it, assign values from one array to another in template and retrieve that values in my controller.

The cause of the exception

The IllegalStateException you are getting is because th:field="${a.id}" in your select element must be related to the form th:object="${project}" element; the th:field attribute must refer to an actual field in the project instance (also you need to write th:field="*{fieldName}" ). That should fix the exception you are getting, but will not solve your entire problem as the second part of it is related to how to make the values get into your controller , which I will explain next.

Sending the values to your controller

To get the values into your controller you will need to make a few changes. As I don't really know the code of your Project class, I will change a few things so you will be able to figure it out how to adapt this simple example to your particular case.

First, I understand you want to make a relation like the following in your form:

  • Role1 => CollaboratorA
  • Role2 => CollaboratorB

Your controller needs to receive a list and in order to receive this information we need two classes:

  • The class which will be storing the individual element data, mapping a role id with the collaborator id:

     public class RoleCollaborator { private Long roleId; private Long collaboratorId; public Long getRoleId() { return roleId; } public void setRoleId(Long roleId) { this.roleId = roleId; } public Long getCollaboratorId() { return collaboratorId; } public void setCollaboratorId(Long collaboratorId) { this.collaboratorId = collaboratorId; } } 
  • A wrapper class to store a list of individual mappings:

     public class RolesCollaborators { private List<RoleCollaborator> rolesCollaborators; public List<RoleCollaborator> getRolesCollaborators() { return rolesCollaborators; } public void setRolesCollaborators(List<RoleCollaborator> rolesCollaborators) { this.rolesCollaborators = rolesCollaborators; } } 

The next thing to do is change your controllers where you have two methods, one that handles GET requests and another one which handles the POST requests and so, receives your form data.

In the GET one:

public String projectCollaborators(@PathVariable Long projectId, Model model) {
    (... your code ...)

    model.addAttribute("project", project);

    // Add the next line to add the "rolesCollaborators" instance
    model.addAttribute("rolesCollaborators", new RolesCollaborators());

    return "project_collaborators";
}

As you can see, we added a line that will be used by the thymeleaf template. Right now is a wrapper of an empty list of roles and collaborators, but you can add values if you need to edit existing mappings instead of adding new ones.

In the POST one:

// We changed the @ModelAttribute from Project to RolesCollaborators
public String projectCollaboratorsPost(@ModelAttribute RolesCollaborators rolesCollaborators, @PathVariable Long projectId, Model model) {
   (... your code ...)
}

At this point, your controller is prepared to receive the information sent from your form, that we also need to modify.

<form th:action="@{'/project_collaborators/' + ${project.id}}" method="post" th:object="${rolesCollaborators}">
    <label th:text="'Edit Collaborators: ' + ${project.name}">Edit Collaborators: Website Project</label>
    <ul class="checkbox-list">

        <li th:each="a, stat : ${assignments}">
            <span th:text="${a.role.name}" class="primary">Developer</span>

            <div class="custom-select">
                <input type="hidden" th:id="rolesCollaborators[__${stat.index}__].roleId" th:name="rolesCollaborators[__${stat.index}__].roleId" th:value="${a.role.id}" />
                <span class="dropdown-arrow"></span>
                <select th:field="*{rolesCollaborators[__${stat.index}__].collaboratorId}">
                    <option th:each="collaborator : ${allCollaborators}" th:value="${collaborator.id}" th:text="${collaborator.name}">Michael Pemulis</option>
                </select>
            </div>
        </li>

    </ul>
    <div class="actions">
        <input type="submit" value="Save" class="button"/>
        <a href="#" class="button button-secondary">Cancel</a>
    </div>
</form>

Here there are a few changes:

  • As I pointed out in The cause of the exception you need to change th:object="${project}" to th:object="${rolesCollaborators}" , as rolesCollaborator is the instance name from where you will receive the values from your GET controller method and where you will be sending the values to your POST controller method.
  • I added a hidden input; this input will store the role id that will be send in association to the collaborator id the user picks from the interface using the select element. Take a look at the syntax used.
  • I changed the th:field value of your select element to refer to a field in the rollesCollaborators object we use in th:object="${rolesCollaborators}" form attribute. This will set the value of the collaborator in the RoleCollaborator element of the RolesCollaborators wrapped list.

With these changes your code will work. Of course, you can improve it with some other modifications, but I tried to not introduce more modifications to focus on your problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM