【问题标题】:Spring MVC + Hibernate: a form with checkboxes for @manytomany relationshipSpring MVC + Hibernate:带有@manytomany 关系复选框的表单
【发布时间】:2014-08-31 15:59:38
【问题描述】:

一周以来,我一直在尝试在 Web 开发中做一件非常简单的事情:一个带有复选框的表单,我可以在其中勾选哪些机构参与了竞赛。

代理商 --> ContestAgency

竞赛类:

// i tried also EAGER
@OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.contest", cascade=CascadeType.ALL) 
private Set<ContestAgency> contestAgencies = new TreeSet<ContestAgency>();

代理类:

@OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.agency", cascade=CascadeType.ALL)
     private Set<ContestAgency> contestAgencies = new TreeSet<ContestAgency>(); 

如何在 WebFlow 或控制器中创建带有复选框的表单?

谢谢

【问题讨论】:

    标签: java hibernate spring-mvc spring-webflow


    【解决方案1】:

    我找到了解决方案。我不认为是最好的,但它有效:

    • 在视图中,我为所有复选框(chk_services、chk_agencies)命名

    • 在过渡到下一步时,我将 POST 生成的逗号分隔字符串与检查的 id (id1, id2, ...) 并传递给一个方法 它拆分字符串,并且对于每个 id,从 db 获取相关实体并将它们放入我的多对多实体集(ContestService)

    (当我进入下一步和上一步时,我都会这样做)

    • 在进入视图时,我想再次选中复选框。为此,我需要将我的 Set 转换为 id 列表:
      < set name="viewScope.checked_services" value="contestFlow.converterSetContestServiceToCheckbox(flowScope.contest.ContestServices)" / >
    
    • 在视图中,我查看是否选中每个复选框:

      th:checked="${#lists.contains(checked_services, '' + service_el.id)}"

    注意:需要 '' + service_el.id,因为没有 '' + 它不起作用



    我发布完整的代码。我希望有人会觉得它有用。

    欢迎任何改进和建议。

    Webapp 配置(我正在使用 java 注释): 使用流程中使用的方法配置 bean

    public class WebAppConfig extends WebMvcConfigurerAdapter {
    
        //...
    
        @Bean
        public ContestFlow contestFlow()
        {
            return new ContestFlow();       
        }
    
    }
    

    ContestFlow bean 与我的方法:

    @Component
    public class ContestFlow {  
    
        static Logger logger = LoggerFactory.getLogger(ContestFlow.class);
    
        @Autowired
        private ServiceService ServiceService;
    
        @Autowired
        private AgencyService AgencyService;
    
        /**
         * input: a comma separated string with all ids checked from the POST
         * 
         * @param contest:      contest object that i will add at the end of the webflow
         * @param ids_string:   comma separated string with checked ids
         * @return
         */
        public Set<ContestService> converterCheckboxToSetContestService(Contest contest, String ids_string)
        {
    
            Set<ContestService> contestServices = new HashSet<ContestService>(0);   
    
            if (ids_string != null)
            {
                String[] arr_ids = ids_string.split(",");
    
                /*
                 * for each record i get the Service
                 */
                for (int i = 0; i < arr_ids.length; i++)
                {
                    try
                    {
                        //get the Service
                        Service service = ServiceService.getService(Integer.parseInt(arr_ids[i]));
    
                        logger.info("Aggiungo il service id [" + arr_ids[i] + "]");
    
                        //creation of the Id object
                        ContestServiceId contestServiceId = new ContestServiceId();             
                        contestServiceId.setService(service);
                        contestServiceId.setContest(contest);
    
                        //record population
                        ContestService contestService = new ContestService();
                        contestService.setService(service);
                        contestService.setContest(contest);
                        contestService.setPk(contestServiceId);
    
                        //add the record
                        contestServices.add(contestService);
    
                    }
                    catch(Exception ex)
                    {
                        ex.printStackTrace();
                        logger.info("Service id [" + arr_ids[i] + "] not found!");
    
                    }       
    
                }       
            }           
            return contestServices;
        }
    
    
        /**
         * input: Set of ContestAgency (many-to-many) checked 
         * and returns a List<String> of ids to be used to select checkboxes 
         * in thymeleaf view with th:checked="${#lists.contains(checked_agencies, '' + agency_el.id)}"
         * 
         * i can't return a List<Integer> because it doesn't check the checkboxes
         * 
         * @param contestAgencies
         * @return
         */
        public List<String> converterSetContestServiceToCheckbox(Set<ContestService> contestServices)
        {
            List<String> result = new ArrayList<String>();
    
            if (contestServices != null)
            {
                Iterator<ContestService> iterator = contestServices.iterator();
                while(iterator.hasNext()) 
                {           
                    ContestService contestService = iterator.next();            
    
                    Integer id = contestService.getService().getId();
    
                    result.add(id.toString());
                }  
            }
    
            return result;
        }
    
    
    
    
        //same as above, for the Agencies:
    
    
    
    
    
        /**
         * input: a comma separated string with all ids checked from the POST
         * 
         * @param contest:      contest object that i will add at the end of the webflow
         * @param ids_string:   comma separated string with checked ids
         * @return
         */
        public Set<ContestAgency> converterCheckboxToSetContestAgency(Contest contest, String ids_string)
        {
    
            Set<ContestAgency> contestAgencies = new HashSet<ContestAgency>(0); 
    
            if (ids_string != null)
            {
                String[] arr_ids = ids_string.split(",");
    
                /*
                 * for each record i get the Agency
                 */
                for (int i = 0; i < arr_ids.length; i++)
                {
                    try
                    {
                        //get the Agency
                        Agency agency = AgencyService.getAgency(Integer.parseInt(arr_ids[i]));
    
                        logger.info("Adding agency id [" + arr_ids[i] + "]");
    
                        //creation of the Id object
                        ContestAgencyId contestAgencyId = new ContestAgencyId();                
                        contestAgencyId.setAgency(agency);
                        contestAgencyId.setContest(contest);
    
                        //record population
                        ContestAgency contestAgency = new ContestAgency();
                        contestAgency.setAgency(agency);
                        contestAgency.setContest(contest);
                        contestAgency.setPk(contestAgencyId);
                        contestAgency.setContractCount(0);  //my many-to-many relationship has an additional field
    
                        //add the record
                        contestAgencies.add(contestAgency);
    
                    }
                    catch(RecordNotFoundException ex)
                    {
                        ex.printStackTrace();
                        logger.info("Agency id [" + arr_ids[i] + "] not found!");
    
                    }       
    
                }       
            }       
            return contestAgencies;
        }
    
    
        /**
         * input: Set of ContestAgency (many-to-many) checked 
         * and returns a List<String> of ids to be used to select checkboxes 
         * in thymeleaf view with th:checked="${#lists.contains(checked_agencies, '' + agency_el.id)}"
         * 
         * i can't return a List<Integer> because it doesn't check the checkboxes
         * 
         * @param contestAgencies
         * @return
         */
        public List<String> converterSetContestAgencyToCheckbox(Set<ContestAgency> contestAgencies)
        {
            List<String> result = new ArrayList<String>();
    
            if (contestAgencies != null)
            {
                Iterator<ContestAgency> iterator = contestAgencies.iterator();
                while(iterator.hasNext()) 
                {
                    ContestAgency contestAgency = iterator.next();
    
                    Integer id = contestAgency.getAgency().getId();
    
                    result.add(id.toString());
                }       
            }   
    
            return result;
        }
    
    }
    

    步骤 2 的视图:带有服务复选框的表单:

    <ul class="list-unstyled">
            <!-- 
            - parent and children are saved in the same table, so i'm not worried about ids overlapping
            -->
    
            <li th:each="service_el : ${services_list}" >
                <input type="checkbox" name="chk_services" th:value="${service_el.id}" th:checked="${#lists.contains(checked_services, '' + service_el.id)}"/>
                <label th:text="${service_el.title}" th:for="'chk_services' + ${service_el.id}">service</label>
    
                <ul class="list-unstyled-padding">
                    <li th:each="subservice_el : ${service_el.children}">
                        <input type="checkbox" name="chk_services" th:value="${subservice.id}" th:checked="${#lists.contains(checked_services, '' + subservice.id)}"/>
                        <label  th:text="${subservice.title}" th:for="'chk_services' + ${service_el.id}">subservice</label>
                    </li>
                </ul>              
            </li>
        </ul>
    

    步骤 3 的视图:带有机构复选框的表单:

    <ul class="list-unstyled">
        <li  th:each="agency_el : ${agencies_list}">
            <input name="chk_agencies" type="checkbox" th:id="'chk_agencies' + ${agency_el.id}" th:value="${agency_el.id}" th:checked="${#lists.contains(checked_agencies, '' + agency_el.id)}" />
            <label th:text="${agency_el.name}" th:for="'chk_agencies' + ${agency_el.id}">agency</label>            
        </li>
    </ul>
    

    最后:流xml配置:

    <?xml version="1.0" encoding="UTF-8"?>
    <flow xmlns="http://www.springframework.org/schema/webflow"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="
            http://www.springframework.org/schema/webflow
            http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
    
        <secured attributes="ROLE_USER" />
    
        <!-- creation of an empty object i will insert in db in the last step -->
        <on-start>
            <evaluate expression="ContestService.createContest()" result="flowScope.contest" />
        </on-start>
    
        <!-- 
            step 1: contest details
         -->
        <view-state id="contest-details" model="contest"> 
            <binder>
                <binding property="startDate" required="true" />
                <binding property="endDate" required="true"/>
                <binding property="bonus" required="true"/>
                <binding property="goal" required="true"/>
                <binding property="title" required="true"/>
            </binder>
            <transition on="proceed" to="contest-services">
            </transition>
            <transition on="cancel" to="cancel" bind="false" />
        </view-state>
    
        <!-- 
            step 2: i select which services are involved
        -->
        <view-state id="contest-services" model="contest">
            <on-entry>      
                <!--
                - in case i'm coming here from the step 3
                - injection of the list of ids previously checked
                -->
                <set name="viewScope.checked_services" value="contestFlow.converterSetContestServiceToCheckbox(flowScope.contest.ContestServices)" />
    
                <!-- 
                 - i get the list of the Main Services
                 - subservices will be scanned with getChildren method  
                 -->
                <set name="viewScope.services_list" value="ServiceService.getMainServices()" />
            </on-entry>         
    
            <transition on="proceed" to="contest-agencies" >
                <!-- 
                - MY SOLUTION TO MANY-TO-MANY checkboxes form:
                - 
                - honestly not very elegant, but in 10 day i could't find better
                - 
                - conversion from String to Set<ContestService>
                -->
                <evaluate expression="contestFlow.converterCheckboxToSetContestService(flowScope.contest, requestParameters.chk_services)" result="flowScope.contest.ContestServices"></evaluate>  
            </transition>
    
            <transition on="cancel" to="contest-details">
                <!-- 
                - also if i go back in the flow, to the first step,
                - i need to remember which checkboxes were selected
                - 
                - and i need to save the checked services to the Contest entity, 
                - else, when i will call addContest method, 
                - it will not save the checked Services
                -->
                <evaluate expression="contestFlow.converterCheckboxToSetContestService(flowScope.contest, requestParameters.chk_services)" result="flowScope.contest.ContestServices"></evaluate>  
            </transition>
        </view-state>
    
    
    
    
        <!-- 
            step 3: i select which agencies are involved in contest.
            only agencies enabled for previously checked services are shown
        -->     
        <view-state id="contest-agencies" model="agencies">
            <on-entry>      
                <!--
                - in case i'm coming here from the step 3
                - injection of the list of ids previously checked
                -->
                <set name="viewScope.checked_agencies" value="contestFlow.converterSetContestAgencyToCheckbox(flowScope.contest.ContestAgencies)" />
    
                <!-- 
                 - only agencies enabled for the step 2 checked services are shown
                 -->
                <set name="viewScope.agencies_list" value="AgencyService.getEnabledAgenciesForServices(contestFlow.converterSetContestServiceToCheckbox(flowScope.contest.ContestServices))" />
            </on-entry>     
    
            <transition on="proceed" to="contest-confirm" >
                <!-- 
                - MY SOLUTION TO MANY-TO-MANY checkboxes form:
                - 
                - honestly not very elegant, but in 10 day i could't find better
                - 
                - conversion from String to Set<ContestAgency>
                -->
                <evaluate expression="contestFlow.converterCheckboxToSetContestAgency(flowScope.contest, requestParameters.chk_agencies)" result="flowScope.contest.ContestAgencies"></evaluate>  
            </transition>
    
            <transition on="cancel" to="contest-services">
                <!-- 
                - MY SOLUTION TO MANY-TO-MANY checkboxes form:
                - 
                - honestly not very elegant, but in 10 day i could't find better
                - 
                - conversion from String to Set<ContestAgency>
                - 
                - and i need to save the checked Agencies to the Contest entity, 
                - else, when i will call addContest method, 
                - it will not save the checked Agencies
                -->
                <evaluate expression="contestFlow.converterCheckboxToSetContestAgency(flowScope.contest, requestParameters.chk_agencies)" result="flowScope.contest.ContestAgencies"></evaluate>  
            </transition>
        </view-state>
    
    
        <!-- 
        - data confirmation before insert in db
         -->    
        <view-state id="contest-confirm" model="contest">       
            <transition on="proceed" to="contest-end" >
                <evaluate expression="ContestService.addContest(contest)" />
            </transition>
            <transition on="cancel" to="contest-agencies" />
        </view-state>   
    
    
    
        <!-- 
        end: redirect to list
         -->
        <end-state id="contest-end" view="externalRedirect:contextRelative:/contest/list"/>
    
        <!-- 
        cancella
         -->
        <end-state id="cancel"/>
    
    </flow>
    

    【讨论】:

      猜你喜欢
      • 2018-01-21
      • 2017-08-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-11
      • 2015-09-25
      • 1970-01-01
      相关资源
      最近更新 更多