[英]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 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.