Spring 3 MVC:动态表单中的一对多(创建/更新时添加/删除) [英] Spring 3 MVC: one-to-many within a dynamic form (add/remove on create/update)

查看:153
本文介绍了Spring 3 MVC:动态表单中的一对多(创建/更新时添加/删除)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在寻找一种解决方案,使用 jQuery 管理HTML表单中的一对多关系。我正在使用 Spring Spring MVC Hibernate 进行开发。我在网络上发现了很多曲目,但没有任何完整的示例。

背景



有三个JPA实体:
$ b $ p code> Consult.java (1)


$ b

  @Entity 
@Table(name =consult)
public class Consult

私人整数ID;
私人字符串标签;
private Set< ConsultTechno> consultTechnos;

/ * getters& setter * /

}

ConsultTechno.java (2)


$ b

  @实体
@Table(name =consult_techno)
public class ConsultTechno {

private Integer id;
私人Techno技术;
私人咨询咨询;
private String level;

/ * getters& setter * /

}

Techno.java (3)



  @实体
@Table(name =techno)
public class Techno {

private Integer id;
私人字符串标签;
private Set< ConsultTechno> consultTechnos;

/ * getters& setters * /

}

如图所示,Consult(1)包含 n ConsultTechnos(2),其特征在于级别和Techno(3)。



使用HTML表单,我想要一个添加一个techno 按钮,它动态地在DOM:



 < input type =textname =consult.consultTechnos []。techno.id/> 
< input type =textname =consult.consultTechnos []。level/>

当然,每次用户点击按钮时,应该重新添加这两个字段,等等。我为这个例子选择了 input type =text,但最后这些字段将是两个 select

应包括四种操作:


  1. 创建新的主实体
  2. 时添加子实体
  3. 创建 strong>新的主实体

  4. 更新新的主实体时添加子实体
  5. 更新新的主实体时移除子实体 问题

    布局部分已经可以工作,但是当发布表单时,我无法设法将动态添加的字段绑定到我的 @ModelAttribute consult



    你有什么想法如何做这种工作?我希望我已经清楚了......



    预先感谢:)

    解决方案

    这一点在网络上仍然相当混乱和不清楚,所以这里是我解决问题的方式。这个解决方案可能不是最优化的解决方案,但是它可以在创建和更新主实体时使用。

    理论




    1. 使用 List 而不是 Set 为您的一对多关系进行动态管理。

    2. 初始化您的列表作为 AutoPopulatingList 。这是一个懒惰的列表,它允许动态添加元素。 添加一个属性 remove int 添加到您的子实体。这将扮演布尔标志的一部分,当动态地移除一个元素时,它将是有用的。 当发布表单时,坚持只有在 0 (即 false )上标记的元素移除


      练习



      工作满例如:雇主有很多雇员,雇员有一个雇主。实体: $ c> Employer.java

        @Entity 
      @表(名称=雇主)
      公共类雇主

      私人整数ID;

      私人字符串名字;
      私人字符串姓氏;
      私人字符串公司;

      私人清单<员工>雇员; //一对多

      / * getters& setters * /

      }

      Employee.java


      $ b

        @Entity 
      @Table(name =员工)
      公共类员工{

      私人整数ID;

      @Transient //表示不是数据库字段
      私有整数删除; //布尔标志

      private String firstname;
      私人字符串姓氏;

      私人雇主雇主; //多对一

      / * getters& setters * /

      }



      控制器:



      EmployerController.java

        @Controller 
      @RequestMapping(employer)
      public class EmployerController {

      //管理动态添加或删除的员工
      私人列表<员工> ; manageEmployees(雇主雇主){
      //存储不应持续的员工
      List< Employee> employees2remove = new ArrayList< Employee>(); (Iterator< Employee> i = employer.getEmployees()。iterator(); i.hasNext();){
      if(employer.getEmployees()!= null){
      Employee employee = i.next();
      //如果remove标志为true,则从列表中删除雇员
      if(employee.getRemove()== 1){
      employees2remove.add(employee);
      i.remove();
      //否则,执行链接
      } else {
      employee.setEmployer(employer);
      }
      }
      }
      return employees2remove;
      }

      // - 创建一个新雇主----------

      @RequestMapping(value =create,method = RequestMethod.GET)
      public String create(@ModelAttribute雇主雇主,模型模型){
      //应该初始化AutoPopulatingList
      return create(employer,model,true);


      私有字符串创建(雇主雇主,模型模型,布尔初始化){
      if(init){
      //初始化AutoPopulatingList
      雇主.setEmployees(new AutoPopulatingList< Employee>(Employee.class));
      }
      model.addAttribute(type,create);
      返回雇主/编辑;

      $ b @RequestMapping(value =create,method = RequestMethod.POST)
      public String create(@Valid @ModelAttribute雇主雇主,BindingResult bindingResult,模型​​模型){
      if(bindingResult.hasErrors()){
      //不应该重新初始化AutoPopulatingList
      return create(employer,model,false);
      }
      //调用私有方法
      manageEmployees(employer);
      //坚持雇主
      employerService.save(雇主);
      returnredirect:employer / show /+ employer.getId();
      }

      // - 更新现有雇主----------

      @RequestMapping(value =update / {pk} ,method = RequestMethod.GET)
      public String update(@PathVariable Integer pk,@ModelAttribute雇主雇主,模型模型){
      //添加您自己的getEmployerById(pk)
      model.addAttribute (类型,更新);
      返回雇主/编辑;
      }

      @RequestMapping(value =update / {pk},method = RequestMethod.POST)
      public String update(@PathVariable Integer pk,@Valid @ModelAttribute Employer雇主,BindingResult bindingResult,模型​​模型){
      //添加您自己的getEmployerById(pk)
      if(bindingResult.hasErrors()){
      return update(pk,employer,model);
      }
      列表< Employee> employees2remove = manageEmployees(雇主);
      //首先,保存雇主
      employerService.update(雇主);
      //然后,删除以前关联的员工,这些员工现在应该被删除
      (Employee employee:employees2remove){
      if(employee.getId()!= null){
      employeeService.delete(雇员);
      }
      }
      返回redirect:employer / show /+ employer.getId();
      }

      // - 显示现有雇主----------

      @RequestMapping(value =show / {pk} ,method = RequestMethod.GET)
      public String show(@PathVariable Integer pk,@ModelAttribute雇主雇主){
      //添加您自己的getEmployerById(pk)
      returnemployer / show ;
      }

      }



      查看:



      employer / edit.jsp

       <%@ taglib prefix =curi =http://java.sun.com/jsp/jstl/core
      %><%@ taglib前缀=formuri =http://www.springframework.org/tags/form
      %><%@ taglib prefix =fnuri =http://java.sun。 com / jsp / jstl / functions
      %><%@ page language =javacontentType =text / html; charset = UTF-8pageEncoding =UTF-8
      % ><!DOCTYPE HTML>
      < html>
      < head>

      < title>编辑< / title>
      < style type =text / css> .hidden {display:none;}< / style>
      < script src =https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js>< / script>
      < script type =text / javascript>
      $(function(){

      //以当前列表的大小开始索引
      var index = $ {fn:length(employer.employees)}; $ b $(b)
      //添加一个新的Employee
      $(#add)。off(click)。on(click,function(){
      $(this)。 before(function(){
      var html ='< div id =employees'+ index +'.wrapperclass =hidden>';
      html + ='< input type =textid =employees'+ index +'.firstnamename =employees ['+ index +'] .firstname/>';
      html + ='< input type =text id =employees'+ index +'.lastnamename =employees ['+ index +'] .lastname/>';
      html + ='< input type =hiddenid = 'employees'+ index +'.removename =employees ['+ index +'] .removevalue =0/>';
      html + ='< a href =# class =employees.removedata-index ='+ index +'>删除< ; / a>';
      html + =< / div>;
      返回html;
      });
      $(#employees+ index +\\.wrapper)。show();
      index ++;
      返回false;
      }); $(b
      $ b)//删除一个员工
      $(a.employees\\.remove)。off(click)。on(click,function(){
      var index2remove = $(this).data(index);
      $(#employees+ index2remove +\\.wrapper)。hide();
      $ (#employees+ index2remove +\\.remove)。val(1);
      return false;
      });

      });
      < / script>

      < / head>
      < body>

      < c:otherwise>< c:set var =actionUrlvalue =employer / update / $ {employer.id}/>< / c:otherwise>

      < form:hidden path =id/>
      < table>
      < tr>
      < td>< form:label path =firstname>名字< / form:label>< / td>
      < td>< form:input path =firstname/>< form:errors path =firstname/>< / td>
      < / tr>
      < tr>
      < td>< form:label path =lastname>姓氏< / form:label>< / td>
      < td>< form:input path =lastname/>< form:errors path =lastname/>< / td>
      < / tr>
      < tr>
      < td>< form:label path =company> company< / form:label>< / td>
      < td>< form:input path =company/>< form:errors path =company/>< / td>
      < / tr>
      < tr>
      < td>员工< / td>
      < td>
      < c:forEach items =$ {employer.employees}varStatus =loop>
      <! - 添加包装div - >
      < c:when test =$ {employer.employees [loop.index] .remove eq 1}>
      < div id =employees $ {loop.index} .wrapperclass =hidden>
      < / c:当>
      < c:其他>
      < div id =employees $ {loop.index} .wrapper>
      < / c:否则>
      <! - 生成字段 - >
      < form:input path =employees [$ {loop.index}]。firstname/>
      < form:input path =employees [$ {loop.index}]。lastname/>
      <! - 添加删除标志 - >
      < c:when test =$ {employees [loop.index] .remove eq 1}>< c:set var =hiddenValuevalue =1/>< / c :当>
      < c:否则>< c:set var =hiddenValuevalue =0/>< / c:否则>
      < form:hidden path =employees [$ {loop.index}]。removevalue =$ {hiddenValue}/>
      <! - 添加链接以删除员工 - >
      < a href =#class =employees.removedata-index =$ {loop.index}>删除< / a>
      < / div>
      < / c:forEach>
      < button id =addtype =button> add< / button>
      < / td>
      < / tr>
      < / table>
      < button type =submit>确定< / button>
      < / form:form>

      < / body>
      < / html>

      希望可以帮助:)


      I'm looking for a solution to manage a one-to-many relation within an HTML form using jQuery. I'm developing with Spring, Spring MVC and Hibernate. I found many tracks on the web, but not any working full-example.

      The background

      I've three JPA entities:

      Consult.java (1)

      @Entity
      @Table(name = "consult")
      public class Consult
      
          private Integer id;
          private String label;
          private Set<ConsultTechno> consultTechnos;
      
          /* getters & setters */
      
      }
      

      ConsultTechno.java (2)

      @Entity
      @Table(name = "consult_techno")
      public class ConsultTechno {
      
          private Integer id;
          private Techno techno;
          private Consult consult;
          private String level;
      
          /* getters & setters */
      
      }
      

      Techno.java (3)

      @Entity
      @Table(name="techno")
      public class Techno {
      
          private Integer id;
          private String label;
          private Set<ConsultTechno> consultTechnos;
      
          /* getters & setters */
      
      }
      

      As shown, a Consult (1) contains n ConsultTechnos (2), which are caracterized by a level and a Techno (3).

      The needs

      Using an HTML form, I would like to have a Add a techno button which dynamically adds two fields in the DOM:

      <input type="text" name="consult.consultTechnos[].techno.id" />
      <input type="text" name="consult.consultTechnos[].level" />
      

      Of course, each time the user clicks on the button, those two fields should be re-added, etc. I chose input type="text" for the example, but at the end, the fields will be two select.

      Four kinds of operation should be covered:

      1. Add a child entity when creating a new master entity
      2. Remove a child entity when creating a new master entity
      3. Add a child entity when updating a new master entity
      4. Remove a child entity when updating a new master entity

      The problem

      That layout part already works, but when posting the form, I can't manage to bind the dynamically added fields to my @ModelAttribute consult.

      Do you have any idea of how to do that kind of jobs? I hope I've been clear enough...

      Thanks in advance :)

      解决方案

      This point is still quite confusing and unclear on the web, so here is the way I solved my problem. This solution is probably not the most optimized one, but it works when creating and updating a master entity.

      Theory

      1. Use a List instead of a Set for your one-to-many relations which should be dynamically managed.

      2. Initialize your List as an AutoPopulatingList. It's a lazy list which allows to add dynamically elements.

      3. Add an attribute remove of int to your child entity. This will play the part of a boolean flag and will be usefull when removing dynamically an element.

      4. When posting the form, persist only the elements that have the flag remove on 0 (i.e. false).

      Practice

      A working full-example: an employer has many employees, an employee has one employer.

      Entities:

      Employer.java

      @Entity
      @Table(name = "employer")
      public class Employer
      
          private Integer id;
      
          private String firstname;
          private String lastname;
          private String company;
      
          private List<Employee> employees; // one-to-many
      
          /* getters & setters */
      
      }
      

      Employee.java

      @Entity
      @Table(name = "employee")
      public class Employee {
      
          private Integer id;
      
          @Transient // means "not a DB field"
          private Integer remove; // boolean flag
      
          private String firstname;
          private String lastname;
      
          private Employer employer; // many-to-one
      
          /* getters & setters */
      
      }
      

      Controller:

      EmployerController.java

      @Controller
      @RequestMapping("employer")
      public class EmployerController {
      
          // Manage dynamically added or removed employees
          private List<Employee> manageEmployees(Employer employer) {
              // Store the employees which shouldn't be persisted
              List<Employee> employees2remove = new ArrayList<Employee>();
              if (employer.getEmployees() != null) {
                  for (Iterator<Employee> i = employer.getEmployees().iterator(); i.hasNext();) {
                      Employee employee = i.next();
                      // If the remove flag is true, remove the employee from the list
                      if (employee.getRemove() == 1) {
                          employees2remove.add(employee);
                          i.remove();
                      // Otherwise, perform the links
                      } else {
                          employee.setEmployer(employer);
                      }
                  }
              }
              return employees2remove;
          }
      
          // -- Creating a new employer ----------
      
          @RequestMapping(value = "create", method = RequestMethod.GET)
          public String create(@ModelAttribute Employer employer, Model model) {
              // Should init the AutoPopulatingList
              return create(employer, model, true);
          }
      
          private String create(Employer employer, Model model, boolean init) {
              if (init) {
                  // Init the AutoPopulatingList
                  employer.setEmployees(new AutoPopulatingList<Employee>(Employee.class));
              }
              model.addAttribute("type", "create");
              return "employer/edit";
          }
      
          @RequestMapping(value = "create", method = RequestMethod.POST)
          public String create(@Valid @ModelAttribute Employer employer, BindingResult bindingResult, Model model) {
              if (bindingResult.hasErrors()) {
                  // Should not re-init the AutoPopulatingList
                  return create(employer, model, false);
              }
              // Call the private method
              manageEmployees(employer);
              // Persist the employer
              employerService.save(employer);
              return "redirect:employer/show/" + employer.getId();
          }
      
          // -- Updating an existing employer ----------
      
          @RequestMapping(value = "update/{pk}", method = RequestMethod.GET)
          public String update(@PathVariable Integer pk, @ModelAttribute Employer employer, Model model) {
              // Add your own getEmployerById(pk)
              model.addAttribute("type", "update");
              return "employer/edit";
          }
      
          @RequestMapping(value = "update/{pk}", method = RequestMethod.POST)
          public String update(@PathVariable Integer pk, @Valid @ModelAttribute Employer employer, BindingResult bindingResult, Model model) {
              // Add your own getEmployerById(pk)
              if (bindingResult.hasErrors()) {
                  return update(pk, employer, model);
              }
              List<Employee> employees2remove = manageEmployees(employer);
              // First, save the employer
              employerService.update(employer);
              // Then, delete the previously linked employees which should be now removed
              for (Employee employee : employees2remove) {
                  if (employee.getId() != null) {
                      employeeService.delete(employee);
                  }
              }
              return "redirect:employer/show/" + employer.getId();
          }
      
          // -- Show an existing employer ----------
      
          @RequestMapping(value = "show/{pk}", method = RequestMethod.GET)
          public String show(@PathVariable Integer pk, @ModelAttribute Employer employer) {
              // Add your own getEmployerById(pk)
              return "employer/show";
          }
      
      }
      

      View:

      employer/edit.jsp

      <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"
      %><%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"
      %><%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"
      %><%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"
      %><!DOCTYPE HTML>
      <html>
      <head>
      
          <title>Edit</title>
          <style type="text/css">.hidden {display: none;}</style>
          <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>
          <script type="text/javascript">
          $(function() {
      
              // Start indexing at the size of the current list
              var index = ${fn:length(employer.employees)};
      
              // Add a new Employee
              $("#add").off("click").on("click", function() {
                  $(this).before(function() {
                      var html = '<div id="employees' + index + '.wrapper" class="hidden">';                    
                      html += '<input type="text" id="employees' + index + '.firstname" name="employees[' + index + '].firstname" />';
                      html += '<input type="text" id="employees' + index + '.lastname" name="employees[' + index + '].lastname" />';
                      html += '<input type="hidden" id="employees' + index + '.remove" name="employees[' + index + '].remove" value="0" />';
                      html += '<a href="#" class="employees.remove" data-index="' + index + '">remove</a>';                    
                      html += "</div>";
                      return html;
                  });
                  $("#employees" + index + "\\.wrapper").show();
                  index++;
                  return false;
              });
      
              // Remove an Employee
              $("a.employees\\.remove").off("click").on("click", function() {
                  var index2remove = $(this).data("index");
                  $("#employees" + index2remove + "\\.wrapper").hide();
                  $("#employees" + index2remove + "\\.remove").val("1");
                  return false;
              });
      
          });
          </script>
      
      </head>
      <body>
      
          <c:choose>
              <c:when test="${type eq 'create'}"><c:set var="actionUrl" value="employer/create" /></c:when>
              <c:otherwise><c:set var="actionUrl" value="employer/update/${employer.id}" /></c:otherwise>
          </c:choose>
      
          <form:form action="${actionUrl}" modelAttribute="employer" method="POST" name="employer">
              <form:hidden path="id" />
              <table>
                  <tr>
                      <td><form:label path="firstname">Firstname</form:label></td>
                      <td><form:input path="firstname" /><form:errors path="firstname" /></td>
                  </tr>
                  <tr>
                      <td><form:label path="lastname">Lastname</form:label></td>
                      <td><form:input path="lastname" /><form:errors path="lastname" /></td>
                  </tr>
                  <tr>
                      <td><form:label path="company">company</form:label></td>
                      <td><form:input path="company" /><form:errors path="company" /></td>
                  </tr>
                  <tr>
                      <td>Employees</td>
                      <td>
                          <c:forEach items="${employer.employees}" varStatus="loop">
                              <!-- Add a wrapping div -->
                              <c:choose>
                                  <c:when test="${employer.employees[loop.index].remove eq 1}">
                                      <div id="employees${loop.index}.wrapper" class="hidden">
                                  </c:when>
                                  <c:otherwise>
                                      <div id="employees${loop.index}.wrapper">
                                  </c:otherwise>
                              </c:choose>
                                  <!-- Generate the fields -->
                                  <form:input path="employees[${loop.index}].firstname" />
                                  <form:input path="employees[${loop.index}].lastname" />
                                  <!-- Add the remove flag -->
                                  <c:choose>
                                      <c:when test="${employees[loop.index].remove eq 1}"><c:set var="hiddenValue" value="1" /></c:when>
                                      <c:otherwise><c:set var="hiddenValue" value="0" /></c:otherwise>
                                  </c:choose>
                                  <form:hidden path="employees[${loop.index}].remove" value="${hiddenValue}" />
                                  <!-- Add a link to remove the Employee -->
                                  <a href="#" class="employees.remove" data-index="${loop.index}">remove</a>
                              </div>
                          </c:forEach>
                          <button id="add" type="button">add</button>
                      </td>
                  </tr>
              </table>
              <button type="submit">OK</button>
          </form:form>
      
      </body>
      </html>
      

      Hope that could help :)

      这篇关于Spring 3 MVC:动态表单中的一对多(创建/更新时添加/删除)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆