如何将旧JSP的代码片段重构为JSF等效代码



原始JSP(WorkItem.JSP)

<c:forEach var="actionItem" items="${workItem.work_action_list}">
<c:if test="${actionItem.workActionClass.work_action_type_id == '1'}" >
<%@ include file="inc_done_button.jsp" %>
</c:if>
<c:if test="${actionItem.workActionClass.work_action_type_id == '2'}" >
<c:set var="actionItem" value="${actionItem}" scope="request" />
<c:set var="checklist" value="${actionItem.meat}" scope="request" />
<jsp:include page="inc_dynamic_checklist_v.jsp" flush="true" />
</c:if>
etc...
</c:forEach>

原始Java

for (ListIterator<WorkflowInstanceWorkItemAction> actionIter = wfiwi.getWork_action_list().listIterator(); actionIter.hasNext();) {
if ("2".equals(work_action_type_id)) {
ChecklistInstanceForm ciForm = new ChecklistInstanceForm(this, authenticatedUser);
ChecklistInstance ci = null; 
ci = (ChecklistInstance) ciForm.getChkLstInstanceByWfiWiaOwner(wfiWorkItemAction, authenticatedUser);
// Get the meat details for this action and inject it into the object
wfiWorkItemAction.setMeat(ci);
}
}
request.setAttribute("workItem", wfiwi);
request.setAttribute("workFlowInstance", wfi); 

新JSF(WorkItem.xhtml)

<f:metadata>
<o:viewParam name="wfi_wid" value="#{workItemController.wfiwi}" converter="#{workItemConverter}"
<f:event type="preRenderView" listener="#{workItemController.preRender}" />
</f:metadata>
<ui:repeat var="actionItem" value="#{workItemController.wfiwi.work_action_list}">
<ui:fragment rendered="#{actionItem.workActionClass.workActionType.action_type_id == '1'}">
<stk:done_button actionItem="#{actionItem}" /> <!-- Here I chose custom c -->
</ui:fragment>
<ui:fragment rendered="#{actionItem.workActionClass.workActionType.action_type_id == '2'}">
<ui:include src="inc_dynamic_checklist.xhtml">
<ui:param name="checklist" value="#{actionItem.meat}" />
</ui:include>
</ui:fragment>

我的新背豆的原料

public class WorkItemController implements Serializable {
private static final long serialVersionUID = 1L;
private WorkflowInstanceWorkItem wfiwi;
public void preRender() {
if (wfiwi.getWork_action_list() != null) {
//loop through and add real model to meat attribute

我追求的是一种更优雅的方式,将模型(我称之为肉)注入到我的每一个动作的视图中。在一个工作项(单页视图)下,有多个操作。作为检查表的操作可以是各种类型(是/否/na、主要/次要数量、是/否-na/已解决等)。

复合组件done_button是直接的,因为我只访问基本的action模型,而没有访问meat。例如done_button.xhtml复合组件的片段

<ui:fragment rendered="#{cc.attrs.actionItem.is_active != '1'}">
Action is not active for you until the following has been completed:
<h:outputText value="#{cc.attrs.actionItem.prerequisite_work_action_list}" escapeXml="false" />
</ui:fragment>

但是dynamic_checklist facelet代码的包含让我感到困惑,因为我将各种Objects注入该通用属性meat:)的方法似乎是错误的。在我最初的JSP中,我使用了<c:set var="checklist" value="${actionItem.meat}" scope="request" />,然后inc_dynamic_checklist_v.jsp的原始JSP看起来有点像

inc_dynamic_checklist_v.jsp

<form method="post" >
<c:out value="${actionItem.workActionClass.name}" /> 
<c:if test="${checklist.checkListClass.type == '1'}" >
<%@ include file="inc_yes_no_na_resolved_checklist.jsp" %>
</c:if>
<c:if test="${checklist.checkListClass.type == '2'}" >
<%@ include file="inc_major_minor_checklist.jsp" %>
</c:if>
<c:if test="${checklist.checkListClass.type == '3'}" >
<%@ include file="inc_quantity_checklist.jsp" %>
</c:if>
<c:if test="${checklist.checkListClass.type == '4'}" >
<%@ include file="inc_yes_no_na_checklist.jsp" %>
</c:if>

这些还包括访问actionItem.meat所需的权限,该权限是使用WorkItem.jsp 中的c:set设置的

我正在寻找指导,是的,我应该将所有这些include转换为复合组件,即使我已经嵌套了include。或者我应该使用基本的ui:includes?我知道我可以用include或cc发送param,但我在模型中是否仍然使用通用字段private Object meat,或者是否有更好的方法来检索这些单独的动作模型。

也许是这样,但它不起作用

<ui:include src="inc_dynamic_checklist.xhtml" >
<ui:param name="wfi_id" value="#{actionItem.workflowInstance.workflow_instance_id}" />
<ui:param name="wfi_aid" value="#{actionItem.wfi_work_item_action_id}" />
</ui:include>

然后在inc_dynamic_checklist.xhtml 中

<f:metadata>
<o:viewParam name="wfi_id" value="#{checklistInstanceView.ci}" converter="#{checklistInstanceConverter}">
<f:attribute name="wfi_id" value="#{param.wfi_id}" />
<f:attribute name="wfi_aid" value="#{param.wfi_aid}" />
</o:viewParam>
</f:metadata>

更新

工作项支持bean。工作项包含一组操作。操作可以是按钮(操作类型id=1)检查表(操作类型id=2),以及其他未实现/显示的内容。我现在所做的是有效的,但这是正确的方式吗?

public void preRender() {
if (wfiwi.getWork_action_list() != null) {
for (ListIterator<WorkflowInstanceWorkItemAction> actionIter = wfiwi.getWork_action_list().listIterator(); actionIter.hasNext();) {
WorkflowInstanceWorkItemAction wfiWorkItemAction = new WorkflowInstanceWorkItemAction();
wfiWorkItemAction = actionIter.next();
Long work_action_type_id = wfiWorkItemAction.getWorkActionClass().getWorkActionType().getAction_type_id();
updatePrerequisites(wfiWorkItemAction, wfiwi.getWorkflowInstance(), wfiwi);
if (work_action_type_id == 2) {
System.out.println("Action Type 2 is Dynamic Checklist Type");
ci = ciRepository.retrieveLatestByWfiWiai(wfiwi.getWorkflowInstance().getWorkflow_instance_id(), wfiWorkItemAction.getWfi_work_item_action_id());
if (ci != null) {
if ("1".equals(ci.getCheckListClass().getType())) {
List<YesNoNaResolvedAnswer> answer_attribute_list = yesNoNaResolvedDao.retrieveByCiWfi(ci.getChecklist_instance_id(), ci.getWorkflowInstance().getWorkflow_instance_id());
ci.setAnswer_attribute_list(answer_attribute_list);
}
if ("2".equals(ci.getCheckListClass().getType())) {
List<MajorMinorAnswer> answer_attribute_list = majorMinorAnsDao.retrieveByCiWfi(ci.getChecklist_instance_id(), ci.getWorkflowInstance().getWorkflow_instance_id());
ci.setAnswer_attribute_list(answer_attribute_list);
}
if ("3".equals(ci.getCheckListClass().getType())) {
List<QuantityAnswer> answer_attribute_list = quantityAnsDao.retrieveByCiWfi(ci.getChecklist_instance_id(), ci.getWorkflowInstance().getWorkflow_instance_id());
ci.setAnswer_attribute_list(answer_attribute_list);
}
if ("4".equals(ci.getCheckListClass().getType())) {
List<YesNoNaAnswer> answer_attribute_list = yesNoNaAnsDao.retrieveByCiWfi(ci.getChecklist_instance_id(), ci.getWorkflowInstance().getWorkflow_instance_id());
ci.setAnswer_attribute_list(answer_attribute_list);
}
wfiWorkItemAction.setMeat(ci);
} else {
Messages.addFlashErrorMessage("Could not find checklist Instance");
}
// wfi_action_list.add(ci);
} else {
wfiWorkItemAction.setMeat("meat pie");
}
}
}

}

inc_dynamic_checklist.xhtml(请参阅上面的WorkItem.xhtm了解其包含方式)这显示了"肉">

<ui:fragment rendered="#{checklist.checkListClass.type == '1'}">
<ui:include src="inc_yes_no_na_resolved_checklist.xhtml" />
</ui:fragment>
<ui:fragment rendered="#{checklist.checkListClass.type == '2'}">
<ui:include src="inc_major_minor_checklist.xhtml" />
</ui:fragment>
<ui:fragment rendered="${checklist.checkListClass.type == '3'}">
<ui:include src="inc_quantity_checklist.xhtml" />
</ui:fragment>
<ui:fragment rendered="${checklist.checkListClass.type == '4'}">
<ui:include src="inc_yes_no_na_checklist.xhtml" />
</ui:fragment>

型号

@Entity
public class WorkflowInstanceWorkItemAction implements Serializable {
private static final long serialVersionUID = 1L;
private String status;
private String is_active;
@Transient
private Object meat; 
and various mappings

一步一个脚印。

重要的是,在进入下一步之前,一切都要按照预期进行。


继续使用JSTL动态构建视图

只需继续使用JSTL,只使用<ui:include>替换JSP includes,直到一切正常为止。现在还不要改变太多。首先让它全部工作起来,然后重构到标记文件或复合文件中。

在最初的JSP方法中,您基本上是在JSTL的帮助下动态构建视图。您可以在JSF 2.x中继续执行同样的操作,前提是您使用的是最新的JSF impl版本,以防止视图范围的bean(Mojarra 2.1.18+)损坏。您可以继续在JSF中使用<c:forEach><c:if><c:set>。您只需要将@include<jsp:include>替换为<ui:include>。请注意,<ui:include>与JSTL具有相同的生命周期。它也是一个标记处理程序,而不是一个组件。另请参阅JSF2 Facelets中的JSTL。。。有道理吗?

然而,<ui:fragment>是一个UI组件。它不会有条件地构建视图。不管它的rendered属性的结果如何,它和它的所有子级最终仍将出现在JSF组件树中。它们只会在呈现响应阶段有条件地呈现HTML输出。与<c:if>相比,JSF组件树的大小会随着每个条件而增长。如果inc_dynamic_checklist_v文件中有4个条件包含,那么它的大小至少会增加4倍。只需继续使用JSTL来动态构建视图。这是一个非常好的工具。另请参阅a.o.如何制作JSF复合组件的网格?另一种选择是通过bindingfindComponent()createComponent()new SomeComponent()getChildren().add()等在backingbean中手动创建组件,这只会导致冗长而脆弱的代码,难以维护。绝对不要那样做。

失败尝试中显示的<f|o:viewParam>具有不同的用途。它们不能像您所期望的那样对<ui:include>中的<ui:param>值执行操作。它们仅对HTTP请求参数起作用。另请参阅What can<f: 元数据>lt;f: viewParam>并且<f: viewAction>用于?对于<ui:include>,您可以继续使用<ui:param>而不是<c:set>,但您应该直接访问它们,就像使用<c:set>一样。唯一的区别是,这些变量只在include本身内部可用,而不是在整个请求中可用(即也在include之外)。<ui:param>的JSP等价物是<jsp:param>,您实际上应该首先使用它。

至于backingbean逻辑,只需将预处理Java代码放在backingbean的@PostConstruct中,将后处理Java代码放置在backingBean的操作方法中,与<h:commandXxx>组件绑定即可。<f:viewAction>preRenderView是不可修改的,因为它们在视图构建时间之后运行得很长,因此JSTL无法获得预期的模型。仅用于处理用户提交的HTTP请求参数。

如果您在旧的Mojarra版本中被鸡蛋视图状态错误所困扰,并且您绝对无法升级,也无法通过将javax.faces.PARTIAL_STATE_SAVING设置为false来禁用部分状态保存,那么您就无法将JSTL标记属性附加到视图范围的bean属性。如果您在这里确实有一个视图范围的bean,并且不能在这里使用请求范围的bean的选项,那么您需要删除JSTL,只使用<ui:repeat><ui:fragment>,而不是<c:forEach><c:if>。但是,您可以继续使用<c:set>(如果适用)。您还应该保留如上所述的支持bean逻辑的指导原则。


用params将重复include重构为标记文件

一旦一切就绪,您就可以开始查看带有params的重复include(即多次使用的<ui:include><ui:param>块),并通过在your.taglib.xml文件中注册它们来将它们重构为标记文件。这实际上并没有改变逻辑和流程,而是使代码更加简洁明了。另请参阅如何创建自定义Facelets标记?用于完整的CCD_ 50示例和在CCD_。

这个虚构的例子包括一个"是/否/na检查表">

<ui:include src="/WEB-INF/includes/tristateChecklist.xhtml">
<ui:param name="value" value="#{actionItem}" />
</ui:include>

可用作

<my:tristateChecklist value="#{actionItem}" />

在将物理文件移动到CCD_ 52中并且如下在CCD_。

<tag>
<tag-name>tristateChecklist</tag-name>
<source>tags/tristateChecklist.xhtml</source>
<attribute>
<name>value</name>
<type>java.lang.Object</type><!-- TODO: fix type -->
</attribute>
</tag>

(你没有展示你的模型,所以我只是指定了一个过于通用的类型)


将重复模型预处理/后处理重构为复合材料

一旦一切恢复正常,您就可以开始查看重复的模型预处理/后处理,并使用"支持组件"将它们重构为组合,以及<cc:implementation>中的相关XHTML。

基本上,当您在@PostConstruct中有相当多的Java代码来将服务/DB返回的"外部"模型转换为视图所期望的"内部"模型时,和/或当您有相当多Java代码在操作中的方法来将"内部"模式转换回服务/DB所期望的"外部"模式时,然后您可以考虑将其重构为可重用的复合组件。这样,当您希望在不同的视图中重用相同的功能时,就不需要将此预处理/后处理任务复制粘贴/重复到不同的backingbean中。并且,您最终会得到一个视图,该视图完全引用了"外部"模型类型,而不是"内部"模型类型(可能由多个属性组成)。

如果没有对所有模型预处理/后处理的完整概述,很难用具体案例的例子来回答这一部分。下面的答案包含了一些例子,这些例子应该对复合成分的意义和意义提供足够的见解:

  • 在两个h:inputText字段上拆分java.util.Date,用f:convertDateTime表示小时和分钟
  • 根据提供的属性初始化复合组件
  • #{cc.clientId}在升级到JSF 2.2后在错误的组合中评估

至少,我觉得你的"肉"可以是一个界面。如果您有具有相同公共行为的不同对象/类,那么您应该创建一个定义该公共行为的接口,并让这些类实现该接口。这部分与JSF并不严格相关,而只是"基本"Java。


别忘了:一步一个脚印。

使用标记文件和组合作为重构工具,最大限度地减少代码重复。您应该已经有了完整的工作代码。

最新更新