37

jQueryを使用してHTMLフォーム内の1対多の関係を管理するためのソリューションを探しています。私はSpringSpring MVCHibernateで開発しています。Webで多くのトラックを見つけましたが、完全な例ではありませんでした。

背景

私は3つのJPAエンティティを持っています:

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 */

}

示されているように、Consult(1)にはn個のConsultTechnos(2)が含まれており、これらはレベルとTechno(3)によって特徴付けられます。

ニーズ

HTMLフォームを使用してAdd a techno、DOMに2つのフィールドを動的に追加するボタンが必要です。

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

もちろん、ユーザーがボタンをクリックするたびに、これら2つのフィールドを再度追加する必要があります。input type="text"例として選択しましたが、最終的にはフィールドは2つになりますselect

4種類の操作をカバーする必要があります。

  1. 新しいマスターエンティティを作成するときに子エンティティを追加します
  2. 新しいマスターエンティティを作成するときに子エンティティを削除します
  3. 新しいマスターエンティティを更新するときに子エンティティを追加する
  4. 新しいマスターエンティティを更新するときに子エンティティを削除する

問題

そのレイアウト部分はすでに機能していますが、フォームを投稿するときに、動的に追加されたフィールドをにバインドすることができません@ModelAttribute consult

そのような仕事をする方法について何か考えがありますか?私は十分に明確になっていると思います...

前もって感謝します:)

4

3 に答える 3

44

この点はまだウェブ上ではかなり混乱していて不明確なので、これが私の問題を解決する方法です。このソリューションはおそらく最も最適化されたソリューションではありませんが、マスターエンティティを作成および更新するときに機能します。

仮説

  1. 動的に管理する必要がある1対多の関係には、のList代わりにを使用します。Set

  2. Listとして初期化しますAutoPopulatingList。これは、要素を動的に追加できる怠惰なリストです。

  3. removeの属性をint子エンティティに追加します。これはブールフラグの役割を果たし、要素を動的に削除するときに役立ちます。

  4. フォームを投稿するときは、フラグが付いている要素のみを保持しますremove0つまりfalse)。

練習

実例:雇用主には多くの従業員がいて、従業員には1人の雇用主がいます。

エンティティ:

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 */

}

コントローラ:

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";
    }

}

意見:

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>

それが役立つことを願っています:)

于 2012-04-03T12:21:27.873 に答える
1

Springtaglibフォームの代わりにHTML入力タグを使用している理由

   <form:form action="yourURL.htm" command="employeeDto">
    <form:input type="text" name="consult.consultTechnos[].techno.id" />
    <form:input type="text" name="consult.consultTechnos[].level" /> 
   <form:form>

のような1つのEmployeeDtoクラスを作成modelMap.addAttribute("employeeDto", new Employee);し、コントローラーに追加します

于 2013-09-04T18:36:34.193 に答える
0

さて、私はちょうど問題に行き着きました、htmlビューソースは追加されたダイナミックhtmlを表示しません。html要素を調べると、DOMツリーに追加されたすべての動的要素が表示されますが、フォーム送信では、動的に作成された要素も含めてすべての要素がサーバーに送信されます。

再現する1つの方法は、ob form submitがjavascriptメソッドを呼び出し、javascriptメソッドにデバッグポイントを置き、DOMツリーを調べてdocument>form要素のformsubmit値を確認することです。

于 2016-09-22T15:47:57.320 に答える