簡體   English   中英

Spring MVC + Hibernate:帶有@manytomany關系復選框的表單

[英]Spring MVC + Hibernate: a form with checkboxes for @manytomany relationship

這是一周以來,我正在嘗試在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或控制器中創建帶有復選框的表單?

謝謝

我找到了解決方案。 我認為不是最好的,但是它可以工作:

  • 在視圖中,我為所有復選框命名(chk_services,chk_agencies)

  • 在過渡到下一步時,我采用從POST生成的逗號分隔的字符串,其中包含已檢查的ID(ID1,ID2,...),然后傳遞給一個拆分字符串的方法,對於每個ID,從db獲取相關信息實體並將其放入我的多對多實體集(ContestService)

<評估expression =“ contestFlow.converterCheckboxToSetContestService(flowScope.contest,requestParameters.chk_services)” result =“ flowScope.contest.ContestServices”> </評估>

(我在進行下一步和上一步時都這樣做)

  • 在進入視圖時,我想再次檢查復選框。 為此,我需要將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>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM