Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
573 views
in Technique[技术] by (71.8m points)

java - Auto populating Set

Is there anything like AutoPopulatingList but for Set? The data that I want to show is an association that uses Set.

public class Employer implements java.io.Serializable {
     private Set<Employee> employees = new HashSet();
}

I've tried using AutoPopulatingList but in that case I have to use List in hibernate which needs me to specify list-index using Employee.employeeId and whenever I retrieve the employees through Employee later the list would have spaces between element (null elements) depending on the Employee.employeeId.

I need to auto-populate the collection because I need to generate the employees dynamically while creating the Employer. I got the following when I use plain Set: org.springframework.beans.InvalidPropertyException: Invalid property 'employees[0]' of bean class [model.Employer]: Cannot get element with index 0 from Set of size 0, accessed using property path 'employees[0]'

Is there any other solution?

Edit

I'm trying to implement dynamic form

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You can not use Set as a binding target in MVC because it is not possible to create property path for its items.

What you should use

You should use Map<Integer, YourType> when building dynamic forms. What we have implemented many times (so I know it is working) is this:

  • a simple number sequence is used as keys without any connection to the actual items
  • the key sequence is always increasing but doesn't need to be continuous (e.g. in case user will delete the second item, you will end up with 1, 3, 4, ... )
  • if you want to add another item, you just find the highest number and then add form indexed with maxIndex + 1 (always increasing sequence)
  • the Map implementation MUST BE instance of LinkedHashMap so that the iteration order is preserved (Spring is creating this implementation by default if a Map field needs to be autopopulated)
  • the Map must be part of some parent form object (i.e. you can not have Map as the top form object) so that Spring is able to infer the generic types from the property getter

View and JavaScript implementation example

There are many ways how you can work with this. For example we have a special template subform, which is used when we need to dynamically add another subform. This approach is probably a bit more complex to follow:

<form:form action="${formUrl}" method="post" modelAttribute="organizationUsersForm">
    <%-- ... other fields ... --%>
    <div id="userSubforms">
        <c:forEach items="${organizationUsersForm.users.entrySet()}" var="subformEntry">
            <div data-subform-key="${subformEntry.key}">
                <spring:nestedPath path="users['${subformEntry.key}']">
                    <%@ include file="user-subform.jspf" %>
                </spring:nestedPath>
            </div>
        </c:forEach>
    </div>
    <button onclick="addSubform(jQuery('#userSubforms'), 'users', 'user', 'userTemplate');">ADD ANOTHER USER</button>
    <%-- other form fields, submit, etc. --%>
</form:form>

<div class="hide" data-subform-template="user">
    <spring:nestedPath path="userTemplate">
        <%@ include file="user-subform.jspf" %>
    </spring:nestedPath>
</div>

<script>
    function addSubform(subformContainer, subformPath, templateName, templatePath) {
        // Find the sequence number for the new subform
        var existingSubforms = subformContainer.find("[data-subform-key]");
        var subformIndex = (existingSubforms.length != 0) ?
                parseInt(existingSubforms.last().attr("data-subform-key"), 10) + 1 : 0;
        // Create new subform based on the template
        var subform = jQuery('<div data-subform-key="' + subformIndex + '" />').
                append(jQuery("[data-subform-template=" + templateName + "]").children().clone(true));
        // Don't forget to update field names, identifiers and label targets
        subform.find("[name]").each(function(node) {
            this.name = subformPath + "["+ subformIndex +"]." + this.name;
        });
        subform.find("[for^=" + templatePath + "]").each(function(node) {
            this.htmlFor = this.htmlFor.replace(templatePath + ".", subformPath + "["+ subformIndex +"].");
        });
        subform.find("[id^=" + templatePath + "]").each(function(node) {
            this.id = this.id.replace(templatePath + ".", subformPath + "["+ subformIndex +"].");
        });
        // Add the new subform to the form
        subformContainer.append(subform);
    }
</script>

Now you can ask "How can user delete a subform"? This is pretty easy if the subform JSPF contains:

<button onclick="jQuery(this).parents('[data-subform-key]').remove();">DELETE USER</button>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...