2016-07-25 1008 views
2

我想通过使用由Thymeleaf处理的html表单在Spring中生成或绑定Project对象。一切工作到目前为止,只有通过勾选复选框填充角色的rolesNeeded列表字段不起作用。Spring Thymeleaf如何将复选框的值绑定到集合字段

项目类

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}一样,它不起作用。

现在我刚刚收到以下错误:

There was an unexpected error (type=Bad Request, status=400). Validation failed for object='project'. Error count: 1

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

亲切的问候, 弗洛里安

+0

关于错误:最有用的方法可能是在浏览器的调试器中打开“网络”选项卡,然后检查发送的请求数据。 – chrylis

+0

只是一个关于“$ {role.id}”/>和$ {role.name}“>的问题。你能否显示你的Role bean(pojo)类? –

+0

chrylis感谢您的快速评论。看到帖子请求中包含所选角色的角色ID Hey Georges我在我的问题中添加了角色类 – Florian

回答

1

我不得不写我自己的春天转换器从“字符串”到“角色”转换,标志着该类作为一个@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(); 
    } 
} 
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)); 
    } 

} 

我在我的web配置覆盖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); 
    } 
} 

我希望这可以帮助别人:)。

相关问题