【问题标题】:Spring Thymeleaf how to bind values of checkboxes to a collection fieldSpring Thymeleaf 如何将复选框的值绑定到集合字段
【发布时间】:2016-11-29 05:16:06
【问题描述】:

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

项目类

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 是一个集合,我希望能够勾选角色复选框,然后在提交表单时生成一个项目对象,并将角色分配给“rolesNeeded”集合。正如我现在用*{rolesNeeded}{role.id} 所说的那样,它不起作用。

现在我收到以下错误:

出现意外错误(类型=错误请求,状态=400)。 object='project' 的验证失败。错误计数:1

希望有人建议如何解决此问题,或者我如何能够获得更详细的错误消息。

亲切的问候, 弗洛里安

【问题讨论】:

  • 关于错误:最有用的方法可能是在浏览器的调试器中打开“网络”选项卡并检查正在发送的请求数据。
  • 只是一个关于 "${role.id}"/> 和 ${role.name}"> 的问题。你能展示你的 Role bean (pojo)class 吗?
  • chrylis 感谢您的快速评论。我这样做了,我可以看到发布请求包含所选角色的角色 ID。嘿 Georges,我在问题中添加了 Role 类。
  • @Florian 查看此快速教程:g00glen00b.be/spring-form-validation

标签: java spring hibernate collections thymeleaf


【解决方案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();
    }
}

【讨论】:

    【解决方案2】:

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

    @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);
        }
    }
    

    我希望这对某人有帮助:)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-05-27
      • 2015-12-06
      • 2011-03-23
      • 1970-01-01
      • 1970-01-01
      • 2023-03-14
      • 2020-02-28
      相关资源
      最近更新 更多