Spring Thymeleaf如何将复选框的值绑定到集合字段

时间:2016-07-25 18:24:19

标签: java spring hibernate collections thymeleaf

我想通过使用由Thymeleaf处理的html表单在Spring中生成或绑定Project对象。到目前为止一切正常,只有通过勾选复选框来填充角色的rolesNeeded列表字段仍然无效。

Project class

package com.floriantoenjes.instateam.model;

import javax.persistence.*;
import java.util.List;

@Entity
public class Project {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String name;
    private String description;
    private String status;

    @ManyToMany
    private List<Role> rolesNeeded;

    @ManyToMany
    private List<Collaborator> collaborators;

    public Project() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public List<Role> getRolesNeeded() {
        return rolesNeeded;
    }

    public void setRolesNeeded(List<Role> rolesNeeded) {
        this.rolesNeeded = rolesNeeded;
    }

    public List<Collaborator> getCollaborators() {
        return collaborators;
    }

    public void setCollaborators(List<Collaborator> collaborators) {
        this.collaborators = collaborators;
    }
}

角色类

package com.floriantoenjes.instateam.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Role {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String name;

    public Role() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

控制器

package com.floriantoenjes.instateam.web.controller;

import com.floriantoenjes.instateam.model.Project;
import com.floriantoenjes.instateam.model.Role;
import com.floriantoenjes.instateam.service.ProjectService;
import com.floriantoenjes.instateam.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.util.List;

@Controller
public class ProjectController {
    @Autowired
    ProjectService projectService;

    @Autowired
    RoleService roleService;

    @RequestMapping("/add")
    public String newProjectForm(Model model) {
        List<Role> roles =  roleService.findAll();

        model.addAttribute("project", new Project());
        model.addAttribute("roles", roles);

        return "edit_project";
    }

    @RequestMapping(value = "/add", method = RequestMethod.POST)
    public String addProject(Project project) {
        projectService.save(project);
        return "redirect:/index";
    }

}

模板

<!DOCTYPE html>
<html>
    <head th:replace="layout :: head('Edit Project')"></head>
    <body>
        <header>
            <div class="container">
                <div class="site-header">
                    <a class="logo" href="index.html">InstaTeam</a>
                    <a class="new-project button icon-left" href="#"><i class="material-icons">add</i> New Project</a>
                </div>
            </div>
        </header>
        <nav>
            <ul>
                <li class="selected"><a href="index.html">Projects</a></li>
                <li><a href="collaborators.html">Collaborators</a></li>
                <li><a href="roles.html">Roles</a></li>
            </ul>
        </nav>
        <section>
            <div class="container wrapper">
                <form th:object="${project}" action="" method="post">
                    <div>
                        <label for="project_name"> Project Name:</label>
                        <input type="text" th:field="*{name}" name="project_name"/>
                    </div>
                    <div>
                        <label for="project_description">Project Description:</label>
                        <textarea rows="4" th:field="*{description}" name="project_description"></textarea>
                    </div>
                    <div>
                        <label for="project_status">Project Status:</label>
                        <div class="custom-select">
                        <span class="dropdown-arrow"></span>
                            <select th:field="*{status}" name="project_status">
                                <option value="active">Active</option>
                                <option value="archived">Archived</option>
                                <option value="not_started">Not Started</option>
                            </select>
                        </div>
                    </div>
                    <div>
                        <label for="project_roles">Project Roles:</label>
                        <ul class="checkbox-list">
                            <li th:each="role : ${roles}">
                                <input type="checkbox" th:field="*{rolesNeeded}" name="project_roles" th:value="${role.id}"/>
                                <span class="primary" th:text="${role.name}"></span>
                            </li>
                        </ul>
                    </div>
                    <div class="actions">
                        <input type="submit" value="Save" class="button"/>
                        <a href="#" class="button button-secondary">Cancel</a>
                    </div>
                </form>
            </div>
        </section>
    </body>
</html>

正如您所看到的,rolesNeeded是一个集合,我希望能够勾选角色的复选框,然后在提交表单时生成一个Project对象,其角色分配给“rolesNeeded”集合。正如我现在使用*{rolesNeeded}{role.id}所做的那样,它不起作用。

现在我收到以下错误:

  

出现意外错误(type = Bad Request,status = 400)。   object ='project'的验证失败。错误计数:1

希望有人建议如何解决这个问题,或者我是如何得到更详细的错误信息。

亲切的问候, 弗洛里安

2 个答案:

答案 0 :(得分:1)

我必须编写自己的Spring Converter以从“String”转换为“Role”,将该类标记为@Component并创建一个@Bean。然后它就像魅力一样。

@Component
public class StringRoleConverter implements Converter<String, Role> {

    @Override
    public Role convert(String source) {
        Role role = new Role();
        int id = Integer.parseInt(source);
        role.setId(id);
        return role;
    }

    @Bean
    public ConversionService getConversionService() {
        ConversionServiceFactoryBean bean = new ConversionServiceFactoryBean();
        Set<Converter> converters = new HashSet<>();
        converters.add(new StringRoleConverter());
        bean.setConverters(converters);
        return bean.getObject();
    }
}

答案 1 :(得分:0)

就我而言,我创建了两个转换器:

@Component
public class RoleStringConverter implements Converter<Role, String> {

    @Override
    public String convert(Role role) {
        return role.getId().toString();
    }

}

@Component
public class StringRoleConverter implements Converter<String, Role> {

    @Autowired
    private RolesRepository rolesRepository;

    @Override
    public Role convert(String source) {
        return rolesRepository.findOne(Long.parseLong(source));
    }

}

我已将其添加到我的网页配置中,覆盖了addFormatters方法。无需做任何其他事情。

@Configuration
@ComponentScan(value = { "controllers", "services", "converters" })
@Import(value = { ThymeleafConfig.class, i18nConfig.class })
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter{

    @Autowired
    private LocaleChangeInterceptor localeChangeInterceptor;
    @Autowired
    private StringRoleConverter stringRoleConverter;
    @Autowired
    private RoleStringConverter roleStringConverter;

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/403").setViewName("403");
        registry.addViewController("/about").setViewName("frontend/about");
        registry.addViewController("/admin").setViewName("admin/index");
        registry.addViewController("/admin/login").setViewName("admin/login");
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        // habilitar procesamiento de contenido estático
       configurer.enable();
    } 

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(localeChangeInterceptor);
        registry.addInterceptor(new CacheControlHandlerInterceptor());
    }

    @Bean(name="multipartResolver")
    public MultipartResolver provideMultipartResolver(){
        return new StandardServletMultipartResolver();
    }

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(Role.class, String.class, roleStringConverter);
        registry.addConverter(String.class, Role.class, stringRoleConverter);
    }
}

我希望这有助于某人:)。