这是一个星期,我正在尝试在网页开发中做一件非常简单的事情:一个带有复选框的表单,我可以勾选哪些代理商在比赛中被收集。
代理商 - > ContestAgency< - Contest
比赛课程:
// 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或控制器中使用复选框创建表单?
感谢
答案 0 :(得分:2)
我找到了解决方案。我认为不是最好的,但它有效:
在视图中,我给所有复选框命名(chk_services,chk_agencies)
转换到下一步,我将POST生成的逗号分隔字符串与已检查的ID(id1,id2,...)一起传递给方法 拆分字符串,对于每个id,从db获取相关实体并将它们放入我的多对多实体集(ContestService)
&LT;评估 表达=“contestFlow.converterCheckboxToSetContestService(flowScope.contest, requestParameters.chk_services)” 结果= “flowScope.contest.ContestServices” &GT; &LT; / evaluate&gt;
(当我进入下一步和前一步时,我这样做)
< 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();
}
}
使用我的方法参加比赛流程:
@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>